Manual CakePHPVer. 1.3.x Fuente: http://book.cakephp.org Compilador por: Ing. Luis Diaz Agosto de 2010 Índice de contenido Manual CakePHP.......................................................................................................................................1 1 Comenzando con CakePHP..............................................................................................................18 1.1 ¿Qué es CakePHP y por qué hay que utilizarlo?......................................................................18 1.2 Dónde obtener ayuda................................................................................................................19 1.3 Entendiendo Modelo-Vista-Controlador..................................................................................21 2 Principios básicos de CakePHP........................................................................................................23 2.1 Estructura de CakePHP.............................................................................................................23 2.1.1 Extensiones de los Controladores ("Componentes")........................................................23 2.1.2 Extensiones de las Vistas..................................................................................................24 2.1.3 Extensiones de los Modelos..............................................................................................24 2.1.4 Extensiones de la Aplicación............................................................................................25 2.2 Una petición típica de CakePHP...............................................................................................25 2.3 Estructura de archivos de CakePHP.........................................................................................28 2.4 Convenciones de CakePHP .....................................................................................................30 2.4.1 Convenciones de los nombres de archivos y clases..........................................................30 2.4.2 Convenciones de Modelo y de la Base de datos...............................................................30 2.4.3 Convenciones de Controladores.......................................................................................31 2.4.3.1 Consideraciones de URL para nombres de controladores.........................................32 2.4.4 Convenciones de Vistas....................................................................................................33 3 Desarrollando con CakePHP............................................................................................................34 3.1 Requerimientos.........................................................................................................................34 3.2 Preparándose para Instalar........................................................................................................34 3.2.1 Obteniendo CakePHP.......................................................................................................35 3.2.2 Permisos............................................................................................................................35 3.3 Instalación.................................................................................................................................35 3.3.1 Desarrollo..........................................................................................................................36 3.3.2 Producción........................................................................................................................36 3.3.3 Instalación Avanzada.........................................................................................................37 3.3.3.1 Rutas de Clase Adicionales.......................................................................................38 3.3.4 Apache y mod_rewrite (y .htaccess).................................................................................39 3.3.5 Lighttpd y mod_magne.....................................................................................................41 3.3.6 Pretty URLs en nginx........................................................................................................42 3.3.7 URL Rewrites on IIS7 (Windows hosts)...........................................................................43 3.3.8 Enciéndelo.........................................................................................................................44 3.4 Configuración...........................................................................................................................45 3.4.1 Configuración de Base de Datos.......................................................................................45 3.4.2 Configuración del Core.....................................................................................................46 3.4.3 La Clase de Configuración................................................................................................46 3.4.3.1 Métodos de la Clase Configure.................................................................................47 3.4.3.1.1 write...................................................................................................................47 3.4.3.1.2 read....................................................................................................................47 3.4.3.1.3 delete..................................................................................................................48 3.4.3.1.4 load....................................................................................................................48 3.4.3.1.5 version...............................................................................................................48 3.4.3.2 Variables de Configuración Principales de CakePHP...............................................48 3.4.3.3 Constantes de Configuración....................................................................................50 3.4.4 La Clase App.....................................................................................................................50 3.4.4.1 Usando App::import()...............................................................................................50 3.4.4.2 Importando librerías del Core...................................................................................50 3.4.4.3 Importando Controladores, Modelos, Ayudantes, Comportamientos y Componentes ...............................................................................................................................................51 3.4.4.3.2 Cargando Modelos [Model]...............................................................................51 3.4.4.3.3 Cargando Componentes [Components].............................................................51 3.4.4.3.4 Cargando Comportamientos [Behaviors]..........................................................51 3.4.4.3.5 Cargando Ayudantes[Helpers]...........................................................................51 3.4.4.4 Cargando desde Plugins............................................................................................51 3.4.4.5 Cargando Archivos de Terceros.................................................................................52 3.4.4.5.1 Ejemplos de archivos de terceros......................................................................52 3.4.5 Configuración de Rutas.....................................................................................................52 Enrutamiento por Defecto.....................................................................................................52 Parámetros con Nombre.............................................................................................................53 Definir Rutas..............................................................................................................................53 Pasando parámetros a la acción.................................................................................................56 Rutas con prefijos......................................................................................................................57 3.4.5.1 Enrutamiento por defecto..........................................................................................58 3.4.5.2 Passed arguments......................................................................................................58 3.4.5.4 Defining Routes.........................................................................................................59 3.4.5.5 Pasando parámetros a las acciones............................................................................62 3.4.5.6 Prefix Routing...........................................................................................................62 3.4.5.7 Rutas y plugins..........................................................................................................63 3.4.5.8 Extensiones de archivo..............................................................................................64 3.4.5.9 Custom Route classes................................................................................................64 3.4.6 Inflexiones Propias............................................................................................................65 3.4.7 Haciendo Bootstrap de CakePHP.....................................................................................66 3.5 Controladores............................................................................................................................67 3.5.1 The App Controller...........................................................................................................68 3.5.2 The Pages Controller.........................................................................................................68 3.5.3 Atributos del Controlador.................................................................................................69 3.5.3.1 $name........................................................................................................................69 3.5.3.2 $components, $helpers y $uses.................................................................................69 3.5.3.3 Atributos Relacionados con la Página: "$layout" y "$pageTitle".............................70 3.5.3.4 El Atributo de Parámetros ("$params").....................................................................71 3.5.3.4.1 form...................................................................................................................71 3.5.3.4.2 admin.................................................................................................................71 3.5.3.4.4 isAjax.................................................................................................................71 3.5.3.4.5 controller............................................................................................................72 3.5.3.4.6 action.................................................................................................................72 3.5.3.4.7 pass....................................................................................................................72 3.5.3.4.8 url ......................................................................................................................72 3.5.3.4.9 data....................................................................................................................72 3.5.3.4.10 prefix................................................................................................................73 3.5.3.4.11 named...............................................................................................................73 3.5.3.5 Otros Atributos..........................................................................................................73 3.5.3.6 persistModel..............................................................................................................74 3.5.4 Métodos del Controlador..................................................................................................75 3.5.4.1 Interactuando con Vistas...........................................................................................75 3.5.4.1.1 set.......................................................................................................................75 3.5.4.1.2 render.................................................................................................................75 3.5.4.2 Control de Flujo........................................................................................................76 3.5.4.2.1 redirect...............................................................................................................76 3.5.4.2.2 flash...................................................................................................................77 3.5.4.3 Retrollamadas ("Callbacks").....................................................................................77 3.5.4.4 Otros Métodos Útiles................................................................................................78 3.5.4.4.1 constructClasses................................................................................................78 3.5.4.4.2 referer................................................................................................................79 3.5.4.4.3 disableCache......................................................................................................79 3.5.4.4.4 postConditions...................................................................................................79 3.5.4.4.5 paginate..............................................................................................................80 3.5.4.4.6 requestAction.....................................................................................................80 3.5.4.4.7 loadModel..........................................................................................................82 3.6 Componentes............................................................................................................................83 3.6.1 Configuración de componentes.........................................................................................83 3.6.2 Creando Componentes a medida......................................................................................84 3.6.2.1 Añadiendo Componentes a tus Controladores..........................................................84 3.6.2.2 Acceso a clases MVC dentro de componentes..........................................................85 3.6.2.3 Usando Componentes en tu Componente.................................................................86 3.7 Modelos....................................................................................................................................88 3.7.1 La Comprensión de Modelos............................................................................................88 3.7.2 Creando Tablas de Bases de Datos...................................................................................90 3.7.2.1 Asociaciones de Tipo de Dato por Base de Datos.....................................................91 3.7.2.1.1 MySQL..............................................................................................................91 3.7.2.1.2 MySQLi.............................................................................................................91 3.7.2.1.3 ADOdb...............................................................................................................92 3.7.2.1.4 DB2....................................................................................................................92 3.7.2.1.5 Firebird/Interbase..............................................................................................92 3.7.2.1.6 MS SQL.............................................................................................................93 3.7.2.1.7 Oracle................................................................................................................93 3.7.2.1.8 PostgreSQL........................................................................................................94 3.7.2.1.9 SQLite................................................................................................................94 3.7.2.1.10 Sybase..............................................................................................................94 3.7.2.2 Titulos........................................................................................................................95 3.7.2.3 Creado y modificado ("created" y "modified").........................................................95 3.7.2.4 Utilizando UUIDs como Claves Primarias...............................................................95 3.7.3 Recuperando tus Datos.....................................................................................................96 3.7.3.1 find............................................................................................................................96 3.7.3.1.1 find('first')..........................................................................................................97 3.7.3.1.2 find('count')........................................................................................................98 3.7.3.1.3 find('all').............................................................................................................98 3.7.3.1.4 find('list')............................................................................................................99 3.7.3.1.5 find('threaded').................................................................................................101 3.7.3.1.6 find('neighbors')...............................................................................................103 3.7.3.2 findAllBy.................................................................................................................104 3.7.3.3 findBy......................................................................................................................104 3.7.3.4 query........................................................................................................................105 3.7.3.5 field..........................................................................................................................106 3.7.3.6 read().......................................................................................................................106 3.7.3.7 Condiciones Complejas de Búsqueda.....................................................................107 3.7.4 Guardando Tus Datos......................................................................................................110 3.7.4.1 Guardando Datos de Modelos Relacionados (hasOne, hasMany, belongsTo)........114 3.7.4.1.1 counterCache - Cache your count().................................................................116 3.7.4.2 Guardando Datos de Modelos Relacionados (HABTM)........................................117 3.7.5 Borrando Datos...............................................................................................................118 3.7.5.1 del............................................................................................................................118 3.7.5.2 deleteAll..................................................................................................................118 3.7.6 Asociaciones: Enlazando Modelos..................................................................................118 3.7.6.1 Tipos de Relaciones.................................................................................................119 3.7.6.2 hasOne.....................................................................................................................120 3.7.6.3 belongsTo................................................................................................................122 3.7.6.4 hasMany..................................................................................................................124 3.7.6.5 hasAndBelongsToMany (HABTM)........................................................................126 3.7.6.6 Creando y Destruyendo Asociaciones al Vuelo......................................................132 3.7.6.7 Múltiples relaciones al mismo modelo....................................................................134 3.7.6.8 Joining tables...........................................................................................................135 3.7.7 Métodos Callback...........................................................................................................137 3.7.7.1 beforeFind...............................................................................................................137 3.7.7.2 afterFind..................................................................................................................137 3.7.7.3 beforeValidate..........................................................................................................138 3.7.7.4 beforeSave...............................................................................................................139 3.7.7.5 afterSave..................................................................................................................139 3.7.7.6 beforeDelete............................................................................................................140 3.7.7.7 afterDelete...............................................................................................................140 3.7.7.8 onError....................................................................................................................140 3.7.8 Atributos del Modelo......................................................................................................140 3.7.8.1 useDbConfig............................................................................................................140 3.7.8.2 useTable...................................................................................................................141 3.7.8.3 tablePrefix...............................................................................................................141 3.7.8.4 primaryKey..............................................................................................................142 3.7.8.5 displayField.............................................................................................................142 3.7.8.6 recursive..................................................................................................................142 3.7.8.7 order........................................................................................................................143 3.7.8.8 data..........................................................................................................................143 3.7.8.9 _schema...................................................................................................................143 3.7.8.10 validate..................................................................................................................144 3.7.8.11 virtualFields...........................................................................................................144 3.7.8.12 name......................................................................................................................144 3.7.8.13 cacheQueries.........................................................................................................144 3.7.9 Métodos Personalizados y Propiedades..........................................................................145 3.7.9.1 Using virtualFields..................................................................................................145 3.7.10 Virtual fields..................................................................................................................147 3.7.10.1 Creating virtual fields............................................................................................147 3.7.10.2 Using virtual fields................................................................................................148 3.7.10.3 Virtual fields and model aliases.............................................................................149 3.7.10.4 Limitations of virtualFields...................................................................................149 3.7.11 Transactions..................................................................................................................150 3.8 Comportamientos....................................................................................................................150 3.8.1 Utilizando Comportamientos..........................................................................................151 3.8.2 Creando Comportamientos Personalizados....................................................................153 3.8.3 Creating behavior methods.............................................................................................153 3.8.4 Behavior callbacks..........................................................................................................154 3.8.5 Creating a behavior callback...........................................................................................154 3.9 DataSources (fuentes de datos)...............................................................................................155 3.9.1 API básica para DataSources..........................................................................................156 3.9.2 Un ejemplo......................................................................................................................157 3.10 Vistas.....................................................................................................................................160 3.10.1 Plantillas de la Vista......................................................................................................160 3.10.2 Layouts..........................................................................................................................160 3.10.3 Elementos......................................................................................................................163 3.10.3.1 Pasar Variables a un elemento...............................................................................163 3.10.3.2 Cache de Elements................................................................................................165 3.10.3.3 Utilizar Elements de un Plugin..............................................................................165 3.10.4 Métodos de la vista.......................................................................................................165 3.10.4.1 set()........................................................................................................................166 3.10.4.2 getVar()..................................................................................................................166 3.10.4.3 getVars()................................................................................................................166 3.10.4.4 error()....................................................................................................................166 3.10.4.5 element()................................................................................................................166 3.10.4.6 uuid........................................................................................................................167 3.10.4.7 addScript().............................................................................................................167 3.10.5 Temas............................................................................................................................167 3.10.5.1 Increasing performance of plugin and theme assets..............................................168 3.10.6 Vistas de Medios...........................................................................................................169 3.11 Helpers..................................................................................................................................170 3.11.1 Usando Helpers.............................................................................................................170 3.11.2 Creando Helpers............................................................................................................170 3.11.2.1 Including other Helpers.........................................................................................171 3.11.2.2 Callback method....................................................................................................172 3.11.2.3 Usando tu Helper...................................................................................................172 3.11.3 Creando Funcionalidad para todos los Helpers.............................................................172 3.11.4 Helpers del Core............................................................................................................173 3.12 Scaffolding............................................................................................................................174 3.12.1 Creating a simple admin interface with scaffolding.....................................................175 3.12.2 Personalizando Vistas Scaffold ....................................................................................176 3.13 La consola de CakePHP........................................................................................................177 3.13.1 Creando Shells y Tasks.................................................................................................178 3.13.1.1 Creando tus Propios Shells....................................................................................178 3.13.1.2 Tasks (Tareas)........................................................................................................180 3.13.2 Running Shells as cronjobs...........................................................................................181 3.14 Plugins..................................................................................................................................182 3.14.1 Crear un Plugin.............................................................................................................183 3.14.2 Controladores del Plugin...............................................................................................183 3.14.3 Modelos del Plugin.......................................................................................................184 3.14.4 Vistas del Plugin............................................................................................................185 3.14.5 Componentes, Ayudantes y Comportamientos.............................................................185 3.14.6 CSS y Javascript en los Plugins....................................................................................186 3.14.7 Consejos para desarrollar Plugins.................................................................................186 3.15 Constantes y Funciones Globales.........................................................................................187 3.15.1 Funciones globales........................................................................................................187 3.15.1.1 __...........................................................................................................................187 3.15.1.2 a.............................................................................................................................188 3.15.1.3 aa...........................................................................................................................188 3.15.1.4 am..........................................................................................................................188 3.15.1.5 config.....................................................................................................................188 3.15.1.6 convertSlash..........................................................................................................189 3.15.1.7 debug.....................................................................................................................189 3.15.1.8 e.............................................................................................................................189 3.15.1.9 env.........................................................................................................................189 3.15.1.10 fileExistsInPath...................................................................................................189 3.15.1.11 h...........................................................................................................................190 3.15.1.12 ife.........................................................................................................................190 3.15.1.13 low.......................................................................................................................190 3.15.1.14 pr..........................................................................................................................190 3.15.1.15 r............................................................................................................................190 3.15.1.16 stripslashes_deep.................................................................................................190 3.15.1.17 up.........................................................................................................................190 3.15.1.18 uses......................................................................................................................191 3.15.2 Constantes predefinidas................................................................................................191 3.16 Paquetes de terceros (Vendor packages)...............................................................................192 3.16.1 Vendor assets.................................................................................................................192 4 Tareas comunes con CakePHP.......................................................................................................193 4.1 Validación de Datos................................................................................................................193 4.1.1 Reglas Simples................................................................................................................195 4.1.2 Una regla por campo.......................................................................................................195 4.1.2.1 rule...........................................................................................................................196 4.1.2.2 required....................................................................................................................196 4.1.2.3 allowEmpty.............................................................................................................197 4.1.2.4 on.............................................................................................................................197 4.1.2.5 message...................................................................................................................197 4.1.3 Múltiples Reglas por Campo..........................................................................................198 4.1.4 Reglas de Validación Incorporadas.................................................................................200 4.1.4.1 alphaNumeric..........................................................................................................200 4.1.4.2 between....................................................................................................................200 4.1.4.3 blank........................................................................................................................200 4.1.4.4 boolean....................................................................................................................201 4.1.4.5 cc.............................................................................................................................201 4.1.4.6 comparison..............................................................................................................202 4.1.4.7 date..........................................................................................................................203 4.1.4.8 decimal....................................................................................................................203 4.1.4.9 email........................................................................................................................204 4.1.4.10 equalTo..................................................................................................................204 4.1.4.11 extension................................................................................................................204 4.1.4.12 file..........................................................................................................................205 4.1.4.13 ip............................................................................................................................205 4.1.4.14 isUnique................................................................................................................205 4.1.4.15 minLength.............................................................................................................206 4.1.4.16 maxLength.............................................................................................................206 4.1.4.17 money....................................................................................................................206 4.1.4.18 multiple..................................................................................................................207 4.1.4.19 inList......................................................................................................................207 4.1.4.20 numeric..................................................................................................................207 4.1.4.21 notEmpty...............................................................................................................208 4.1.4.22 phone.....................................................................................................................208 4.1.4.23 postal.....................................................................................................................208 4.1.4.24 range......................................................................................................................209 4.1.4.25 ssn..........................................................................................................................209 4.1.4.26 url..........................................................................................................................209 4.1.5 Reglas de Validación Personalizadas..............................................................................210 4.1.5.1 Validación Personalizada Mediante Expresiones Relugares...................................210 4.1.5.2 Validación Mediante Métodos Personalizados........................................................210 4.1.6 Validando datos desde el Controlador.............................................................................211 4.2 Limpieza de Datos..................................................................................................................212 4.2.1 paranoid...........................................................................................................................212 4.2.2 html.................................................................................................................................212 4.2.3 escape..............................................................................................................................213 4.2.4 clean................................................................................................................................213 4.3 Manejo de Errores...................................................................................................................214 4.4 Depuración..............................................................................................................................215 4.4.1 Depuración básica...........................................................................................................215 4.4.2 Usando la clase Debugger...............................................................................................216 4.4.3 Clase Debugger...............................................................................................................218 4.5 Caching...................................................................................................................................219 4.6 Logging...................................................................................................................................219 4.6.1 Uso de la función log......................................................................................................219 4.6.2 Using the default FileLog class.......................................................................................220 4.6.3 Creating and configuring log streams.............................................................................221 4.6.4 Interacting with log streams............................................................................................222 4.6.5 Error logging...................................................................................................................222 4.7 Testing.....................................................................................................................................222 4.7.1 Preparándose para el testing............................................................................................222 4.7.1.1 Installing SimpleTest...............................................................................................223 4.7.1.2 Ejecutando los test-cases incorporados...................................................................223 4.7.2 Introducción a los test - Unit testing vs. Web testing......................................................224 4.7.3 Preparando datos de prueba............................................................................................224 4.7.3.1 Acerca de las fixtures..............................................................................................224 4.7.3.2 Creando fixtures......................................................................................................224 4.7.3.3 Importar información de tabla y registros...............................................................226 4.7.4 Creando los tests.............................................................................................................228 4.7.4.1 CakeTestCase Callback Methods............................................................................229 4.7.5 Testing models................................................................................................................229 4.7.5.1 Creating a test case..................................................................................................229 4.7.5.2 Creating a test method.............................................................................................231 4.7.6 Testing controllers...........................................................................................................231 4.7.6.1 Creando un test case................................................................................................231 4.7.6.2 El método testAction...............................................................................................233 4.7.6.3 Pitfalls......................................................................................................................234 4.7.7 Testing Helpers...............................................................................................................234 4.7.7.1 Creating Helper test, part I......................................................................................234 4.7.8 Probando componentes...................................................................................................235 4.7.8.1 Initializing the component.......................................................................................236 4.7.8.2 Creando un método de prueba.................................................................................236 4.7.9 Web testing - Testeando las vistas...................................................................................237 4.7.9.1 About CakeWebTestCase........................................................................................237 4.7.9.2 Creando un test........................................................................................................237 4.7.9.3 Walking through a page...........................................................................................238 4.7.10 Testing plugins..............................................................................................................239 4.7.11 Miscellaneous................................................................................................................240 4.7.11.1 Customizing the test reporter................................................................................240 4.7.11.2 Test Reporter methods...........................................................................................240 4.7.11.3 Grouping tests........................................................................................................241 4.7.12 Running tests in the Command Line.............................................................................242 4.7.13 Test Suite changes in 1.3...............................................................................................243 4.8 Internacionalización & Localización......................................................................................245 4.8.1 Internacionalizando su aplicación...................................................................................245 4.8.2 Localización en CakePHP...............................................................................................246 4.9 Paginación...............................................................................................................................248 4.9.1 Preparación del controlador............................................................................................248 4.9.2 Pagination in Views........................................................................................................250 4.9.3 Paginación AJAX............................................................................................................251 # Configuring the PaginatorHelper to use a custom helper................................................252 4.9.4 Custom Query Pagination...............................................................................................252 4.10 REST....................................................................................................................................253 4.10.1 The Simple Setup..........................................................................................................254 4.10.2 Custom REST Routing..................................................................................................256 5 Componentes del Núcleo................................................................................................................257 5.1 Listas de Control de Acceso...................................................................................................257 5.1.1 Entendiendo cómo funciona ACL...................................................................................258 5.1.2 Definiendo Permisos: ACL basado en INI......................................................................263 5.1.3 Definiendo Permisos: ACL en la base de datos..............................................................265 5.1.3.1 Comenzando............................................................................................................265 5.1.3.2 Creando Access Request Objects (AROs) y Access Control Objects (ACOs).......266 5.1.3.3 Asignando Permisos................................................................................................272 5.1.3.4 Verificando Permisos: El Componente ACL...........................................................274 5.2 Autenticación..........................................................................................................................275 5.2.1 Configurando las variables del componente Auth..........................................................277 5.2.2 Mostrando Mensajes de Error en la Autenticación.........................................................278 5.2.3 Problemas comunes con Auth.........................................................................................278 5.2.4 Cambiar la Función Hash................................................................................................279 5.2.5 Métodos de AuthComponent..........................................................................................280 5.2.5.1 action.......................................................................................................................280 5.2.5.2 allow........................................................................................................................280 5.2.5.3 deny.........................................................................................................................281 5.2.5.4 hashPasswords.........................................................................................................281 5.2.5.5 mapActions..............................................................................................................282 5.2.5.6 login.........................................................................................................................282 5.2.5.7 logout.......................................................................................................................283 5.2.5.8 password..................................................................................................................283 5.2.5.9 user..........................................................................................................................284 5.2.6 Atributos de AuthComponent.........................................................................................285 5.2.6.1 userModel................................................................................................................285 5.2.6.2 fields........................................................................................................................285 5.2.6.3 userScope................................................................................................................285 5.2.6.4 loginAction..............................................................................................................285 5.2.6.5 loginRedirect...........................................................................................................286 5.2.6.6 logoutRedirect.........................................................................................................286 5.2.6.7 loginError................................................................................................................286 5.2.6.8 authError..................................................................................................................286 5.2.6.9 autoRedirect............................................................................................................287 5.2.6.10 authorize................................................................................................................288 5.2.6.11 sessionKey.............................................................................................................290 5.2.6.12 ajaxLogin...............................................................................................................290 5.2.6.13 authenticate............................................................................................................290 5.2.6.14 actionPath..............................................................................................................290 5.2.6.15 flashElement..........................................................................................................290 5.3 Cookies...................................................................................................................................291 5.3.1 Configuración del Controlador.......................................................................................291 5.3.2 Utilizando el Componente..............................................................................................292 5.4 Email.......................................................................................................................................294 5.4.1 Atributos y Variables de la clase.....................................................................................294 5.4.1.1 Envío múltiple de emails en bucle..........................................................................295 5.4.1.2 Debugging Emails...................................................................................................295 5.4.2 Envío de un mensaje simple...........................................................................................295 5.4.2.1 Configurando el Layout..........................................................................................295 5.4.2.2 Configurar un elemento Email para el cuerpo del mensaje....................................296 5.4.2.3 Controlador..............................................................................................................297 5.4.2.4 Attachments.............................................................................................................298 5.4.3 Enviar un mail por SMTP...............................................................................................298 5.5 Request Handling....................................................................................................................299 5.5.1 Obtaining Request Information.......................................................................................299 5.5.2 Request Type Detection..................................................................................................301 5.5.3 Obtaining Additional Client Information........................................................................302 5.5.4 Responding To Requests.................................................................................................302 5.6 El Componente Security.........................................................................................................304 5.6.1 Configuración.................................................................................................................304 5.6.2 Métodos...........................................................................................................................306 5.6.2.1 requirePost()............................................................................................................306 5.6.2.2 requireSecure()........................................................................................................306 5.6.2.3 requireAuth()...........................................................................................................306 5.6.2.4 requireLogin().........................................................................................................306 5.6.2.5 loginCredentials(string $type).................................................................................306 5.6.2.6 loginRequest(array $options)..................................................................................306 5.6.2.7 parseDigestAuthData(string $digest)......................................................................307 5.6.2.8 generateDigestResponseHash(array $data).............................................................307 5.6.2.9 blackHole(object $controller, string $error)............................................................307 5.6.3 Uso..................................................................................................................................307 5.6.4 Basic HTTP Authentication............................................................................................308 5.7 Sesiones..................................................................................................................................309 5.7.1 Métodos...........................................................................................................................310 5.7.1.1 write.........................................................................................................................310 5.7.1.2 setFlash....................................................................................................................310 5.7.1.3 read..........................................................................................................................311 5.7.1.4 check........................................................................................................................311 5.7.1.5 delete.......................................................................................................................311 5.7.1.6 destroy.....................................................................................................................312 5.7.1.7 error.........................................................................................................................312 6 Core Behaviors (Comportamientos Basicos).................................................................................313 6.1 ACL........................................................................................................................................313 6.1.1 Using the AclBehavior....................................................................................................313 6.1.2 node()..............................................................................................................................315 6.2 Containable.............................................................................................................................315 # Using Containable.................................................................................................................316 # Containing deeper associations.............................................................................................318 6.2.1 Using Containable with pagination.................................................................................320 # ContainableBehavior options................................................................................................321 6.3 Translate..................................................................................................................................322 6.3.1 Inicializando las tablas de la Base de datos i18n............................................................322 6.3.2 Adjuntando el Comportamiento de Traducción a tus Modelos.......................................322 6.3.3 Definiendo los Campos...................................................................................................323 6.3.4 Conclusiones...................................................................................................................323 6.3.5 Obtener todos los registros de traducción para un campo determinado.........................324 6.3.5.1 Using the bindTranslation method..........................................................................325 6.3.6 Saving in another language.............................................................................................326 6.3.7 Multiple Translation Tables............................................................................................327 6.3.7.1 Create the TranslateModel......................................................................................328 6.3.7.2 Changing the Table..................................................................................................328 6.4 Arboles (Tree).........................................................................................................................329 6.4.1 Requerimientos...............................................................................................................329 6.4.2 Uso Básico......................................................................................................................329 6.4.2.1 Agregando datos......................................................................................................331 6.4.2.2 Modificando datos...................................................................................................332 6.4.2.3 Borrando datos........................................................................................................333 6.4.2.4 Haciendo consultas y usando tus datos...................................................................334 6.4.2.4.1 El método children..........................................................................................334 6.4.2.4.2 Contando los hijos...........................................................................................335 6.4.2.4.3 generatetreelist.................................................................................................335 6.4.2.4.4 getparentnode..................................................................................................336 6.4.2.4.5 getpath.............................................................................................................336 6.4.3 Uso Avanzado.................................................................................................................337 6.4.3.1 moveDown..............................................................................................................337 ........1.................3.............................4 $options[‘options’]............1 Generalidades en Caching............................2 Métodos...........369 ..................................1..............6 observeField............1..4 Data Integrity.........3.......................................................................344 7........351 7........................................................................4 Caching en los Controllers......1...........6 $options[‘maxLength’].359 7........................3...2........3 Formularios.........................................4.........................341 6..................4 reorder....4............................2 Cache.......................9 isAjax........................................ $options[‘between’]............................356 7....367 7.................................................................................1 $options[‘type’].............359 7................................................................1 Creando Formularios..................................................................................................................5 7...........339 6..................................1 AJAX............................ $options[‘separator’] and $options[‘after’] .......................................348 7..362 7.......................................................................1...................................1.....................................................................3..................5 Marking Non-Cached Content in Views.......................................369 7..........363 7........................................342 7 Ayudantes del Core......................................................1.....................................356 7.................................................4.............................................................................................................................344 7....................................................366 7....................................................1...........................................2......................3...................................................................3.....3 Verify........3 remoteTimer....2....2...............361 7..........2.2 $options[‘type’]............................................................1..................................2.................................4................................................5 $options[‘multiple’]..................................3.5 $options['inputDefaults']..........................363 7...................................................2 Motores de Cache en Cake.............................345 7........................................................................................................................................................345 7..2..........................343 7........................................................4 form.348 7...1......1............3 removeFromTree............3..........................8 autoComplete..353 7.......338 6...............3......................3.................................4........3...............................................................................2 remoteFunction...........................................3..............................................................................................3.....344 7.............10 drag & drop................................3......1...........340 6.1 General Options...........................................................1...................2....4 $options[‘default’]...........2 moveUp...1...............................2..350 7...........2............................................2..................................................................................................340 6...............1................................................................................................................................1....................................................3......................1.....................................................1 Recover............................................................................................3..............................356 7...........2.......1 Field naming convention.......................365 7...362 7.2 Closing the Form......................................3....2.......................2 Opciones de retrollamadas (Callback Options)....357 7............361 7..........................................................................................2.........340 6..............................................1......................2......2 Reorder....................1...................................1.................................................................................2...............................3 Automagic Form Elementos......4..................349 7..................................................................................................3.............352 7............................................................2 $options[‘action’].........................................................................1.......................1......................................................................343 7...................3.....................2....3............4....12 editor.......................357 7...............................1......347 7.......................3.....366 7.............................6.......................359 7......352 7..........................6 Clearing the Cache...............11 slider...........................4.....................................................................4......1 AjaxHelper Options......................................................354 7.....................................................358 7....................362 7............................................................................................................................3..............3 $options[‘before’]..................................................................................................................2................13 sortable........2...1...3 $options[‘url’]................1 link.......3 Configuracion del Cache Helper.................1..........................3.............3.................355 7.............5 submit.7 observeForm................350 7..................................4....................................... ..........371 7............1..........................................................................................................................................391 7..........381 7.......................4.................3 improvements...3..........................................................392 7...............................................1.....3......................384 7....................20 $options['class']........................5 style..3..........................................................3.............................................376 7....3............18 $options['minYear']........................................................................................3.......................................1 Validating Uploads...... $options['maxYear']............................................................382 7....................................3...........4 month...........10 error..........................................................................................................................4............................................1 Inserting Well-Formatted elements..............................3....3.............................................5..........380 7..................3.................................................5........3..................................371 7..........................................................3.................................................................................374 7......................374 7.5............5......3......380 7.......3.......................................11 file....394 7.................................................................................................................................... $options[‘cols’].............5.............5..............................3.................................................................15 password........................3.........................................................................3.............................................372 7........6 day...................................................................................3...............................372 7..........381 7......4..................5...............................17 $options[‘dateFormat’]...........................3..........................................................4..389 7......................................................................................1.......................9 $options['legend'].....................................................3..............5..................................................4....3..................3......4.388 7.....................................5.........................7 link.......................11 $options['error']............................................................................5.........3.....................18 submit...............................3 meta.....................................13 $options[‘selected’].....................................14 $options[‘rows’]...........3..................................................3........3.............................392 7.......................................1.......................387 7........370 7..4.................................................4.............................................................................................4.................................5.........................371 7......376 7....3...............5...........3...........................................5..................7 hour........................384 7...........388 7........1 charset..............................373 7.......3....................5...................................................4 docType.....................................................................................................................................................8 minute.......10 $options[‘id’]..................373 7..............................................................................................................372 7..........................12 $options['default']........394 .........................................383 7....3....................................................3...378 7......3.13 isFieldError.........19 text...........1...............................................................................5............1 checkbox.3......374 7...........................1.......................3.......3....................................4...............2 css............376 7...3.........................377 7........382 7...............3...............................................................................................................................5......................................................................................................................3 year..3..................4 File Fields.......................................................................................................................................................374 7......3...........7...3..............381 7..................9 div....................20 textarea.....................7 $options[‘div’]................6 image............................................4........................6 1........................8 tag...1.............................................3...........................................................................................3....................................16 radio............3...........5.......................3.........3..............19 $options['interval'].388 7..................3...........................................5 Form Element-Specific Methods..............3.5........................................................1.......15 $options[‘empty’]...5 dateTime...................391 7................3................4 HTML..................................383 7..........................................385 7.............................369 7.8 $options[‘label’]...................14 label.........3.............................374 7...................................................379 7.16 $options[‘timeFormat’]................9 meridian.....17 select........375 7............5...3.........5...............2 button......12 hidden..............................380 7............................................................3..............................................................379 7......................................380 7................380 7....................................1..........3............. .......421 7.................................................................5................................................2 precision........................428 7...........................................................................................9 RSS...........................................................................................................................................................399 7............................................430 # autoLink..............................................................................................................................12 scriptBlock..............................................428 7...................9............................................................................................................418 7.........396 7......................................................................420 7............13 scriptStart..3..............................423 7......1...........................4 toReadableSize......................................................4.................................6 Javascript................................................................................14 scriptEnd...................1...................................................1......400 7....................431 # toList..........................396 7...............10.............5..............1..........................................................................401 7..........................1 Working with buffered scripts................................1.....400 # Using jQuery with other libraries.................................5....................................395 7.............................................................429 # autoLinkUrls....9......................................................428 # Using Flash for Success and Failure.................431 # truncate...............................................................1 Methods..............................1 Código para el Controlador....5 Ajax Pagination.......................................430 # stripLinks.................................................................16 tableCells.....413 # Adding effects and transitions...............................1 Métodos....................................................7.....................................................................430 # excerpt......4.................................3 toPercentage.....................11 script.418 7.....................4 Methods..................................................................9................................................................................8.4....................................................8 Paginator...................415 7...........................1 Using a specific Javascript engine..................................1...............430 # highlight..................4...................7.....................1...........................................................1...........5 format...431 ..................................394 7..............................402 7............................1 Methods.....................1 Layout RSS........10.......................................................................7 Number...................419 7...........................................421 7.........................................................................420 7..................................................................................................................2 flash...........................................................................................................................................................................4..........................................................................3 Javascript engine usage.............429 # autoLinkEmails..............................17 url...401 # Common options...........................................402 7.........7......................................................................................................5 Js..........................15 tableHeaders...................................................................................................................................1................................10 para............................................................................................401 7.............................................................................................................1.......................1.......................................1........................................................424 7...................................................................................................396 7..................................................................................................................................................................................................................397 7....................423 7........................................................................................................................402 # Callback wrapping....2 Changing the tags output by HtmlHelper.424 7....................11 Text.......................................................7.................................................................................................................................................................................................................................................4.........................5.......................................427 7......2 La vista....395 7..................................................................................................................................398 7.............................................5....9.........10 Sesión.....................................4.......................................................................................................................................420 7..........................................................................................................................414 7....................................6.......................................................413 # Making Ajax Links......................................................7....................................1 currency......4................425 7.........................................2 Creating a Javascript Engine.....................................................404 7...................1.....................................1 Creando un RSS feed con el RssHelper.4..5..................................................7.................................415 7.............................................. .....................................................................................5........................................436 8.457 8.................................................................................................................................462 8.........................................17 map...........................................................................................................................................................................................434 7.......................................................................................................................................................439 8.478 8.......................5.......2 Testing Time................................4...........................................................................................13....................................................437 8...4 Xml......3................................2 insert.................439 8.................438 8..............440 8...........................................................................................4 reverse.....5............481 ............3 sort..........................................................7 Cache............435 8 Librerias de utilidades del núcleo.............7.......436 8..............................................11 classicExtract..............442 8....................................................454 8..476 8...................................1 Análisis Xml.........................3.........................................9 check............476 8.............................................................................13.......434 7.1 App....................................................# trim...................................................................................................473 8.............................................432 7...................12................471 8.........5....5..475 8..1 uuid.............................13...........................................5......................................................................................................................1 Formatting.......................................................................................4 Cache::config()..........................................................467 8....................5....................................................................................................................................7.................................................2........................................................................................................................................................437 8...458 8......................................................................................................................................................3.5.......5.........................7.....................................................5...............................5 combine.........437 8.........13 extract.............................469 8......................................................................................................................................................................3.........................................................478 8...................................6 normalize..................................................5.................................................................479 8..........................1 Class methods.......................................5....................3 Cache::delete()........................432 7........................19 filter.....478 8..........8 HttpSocket......438 8...........................1 Set-compatible Path syntax....................................13 XML..............................................................................5..................................1 Cache::read()......................................436 8........................................12 matches................................................................447 8......................468 8.....................................................18 pushDiff....................................................................................2 elem..............................................5..................................................................................................................................480 8..........5...........................................20 merge...........................................1 serialize.....................16 numeric...........................................................................................................................................................5..........................480 8..............................7.............5 Cache::set()...................................3 header.........12..................................2 tokenize............................437 8...................................................................432 7..................................................3 Cadenas (String).................................................5..................5....444 8...................................438 8.7....2 Cache::write()..............................21 contains................................................5............................................10 remove..................................................................................................................................................................................................................................15 enum....478 8....................................................................436 8......................................5......7 countDim...............................................................................................460 8...............................................................................................................................................................................5................................................................................................................................................................434 7..........435 7......................................................................................................................................................................................................3 insert......................479 8............................................................12 Tiempo.................................471 8..................................5 Set...........................................................................................6 Security..............14 format.............................................................8 diff..................................................461 8...................2 Inflector..............4 cleanInsert................................................. ....................................................................................................525 # Configuration and application bootstrapping.........................3 Configuración de la Base de Datos en Cake.....................................2 Migrations with CakePHP schema shell............................................................................................535 # Console and shells....................................1.........................................................1........2....................1 Controller & Components..............3........................................................................................507 11..................................1...............................483 9............................1 Generando y Usando Archivos de Esquemas.................2................8......................2 a 1.............................................483 9.........................................508 11..........................................512 11..........525 # Removed Constants...................10 Todo hecho...........................................1..........490 11......1.............................................................3 Modificando el HTML producido por defecto del script bake...5 Una aclaración para mod_rewrite...............497 11..........521 11.....................................................1...........516 11..........2......................................2.....491 11.......502 11................................3 request...................1 Generación de Código con Bake.............................................................4 Act as a Requester (Solicitante)....................................6 Crear un modelo Post..........................................................................2 Gestión del Esquema de la BBDD y Migraciones...........488 9..............................................................8 Logueo de Usuarios...............................................................................................10 Validación de Datos....................2................533 # View and Helpers..........495 11.......1 Group-only ACL.........528 # Library Classes........................538 ..........................................................................................................2.....................................481 8...........................................................................2 Simple Acl controlled Application..............................................................................................................................................2 post...........................................................12 Editando Posts.......................1 get...................................487 9.........................................................................................................................................................................................505 11...............................4..............492 11...............9 Logout (deslogueo).....1 Obteniendo Cake......................................7 Crear un controlador para Post..............................................................511 11.........490 11................................515 11...2.....484 9.....8.............2...................501 11..................................................................................................................................................504 11...............................................1.........................................................................................5 Creando ACOs.........1...............................................489 10 Deployment......481 8.....................1.............................1..................................3 Inicializar las tablas Acl en la BD.............................529 12..............................................................................2 Preparándose para agregar Autentificación (Auth)......................14 Conclusión........................................2.......................................3....1 11..........................7 Configurando los permisos.......................................................1....................1......................523 11..............................................................................................................................................................................................................1 Bake improvements in 1...................................................................487 9.................8........................................527 12.................1..........4...........................................................................1........2.509 11...............................8..............................................13 Rutas................................................524 12 Apendices..................................................................................................................................................................................................2.....................................................................................4 Configuración Opcional...................................................2..491 11....................................................................................493 11............8 Creando las Vistas(Views) de los Post .513 11..............................................................1....489 11 Ejemplo de Aplicación....1.............................................................1....................................................................509 11......................................................................11 Borrando Posts..............2 Model Databases and Datasources.6 Una herramienta automática para crear ACOs............525 12..............................2 Creando la Base de Datos del Blog..496 11.................9 Agregando Posts......1 Preparando nuestra aplicación................................................2.2........523 11...............................525 # File renames and internal changes.....1 Blog..481 9 Aplicaciones de Consola Principales......1 Migrando desde CakePHP 1........................................................................517 11...............494 11........................................................ .......540 # View & Helpers...........................................545 # Router and Dispatcher...........................................2 Nuevas características en CakePHP 1....................................2.................................546 # Library classes..................................................548 ..................................................................................................................................................................................................................................................................................................................................................... Test Suite & schema.....................................................................................1...........................................................................................3 Vendors.......................................3....................................................................................542 # Caching................................................................................................................543 # Console...........................1 Logging.................... Behaviors and Datasource..541 12........540 # Componentes...............................................................................................546 # Miscellaneous..12...........................................542 # Models...539 12........................................................... Con CakePHP. empieza con lo verdaderamente importante y no reinventes la rueda cada vez que te incorpores a un nuevo proyecto. Se trata de una estructura que sirve de base a los programadores para que éstos puedan crear aplicaciones Web. SQL. CakePHP tiene un equipo de desarrolladores y una comunidad activos. libre. de código abierto.1 Comenzando con CakePHP ¡Bienvenido al Cookbook. el manual para el framework CakePHP que convierte el desarrollo de aplicaciones web en un juego de niños! Este manual supone que posees conocimientos generales de PHP y conocimientos básicos de programación orientada a objetos (POO). lo que añade valor al proyecto. Consigue una copia de CakePHP. El manual no pretende explicar dichas tecnologías. Esta es una lista breve con las características de las que disfrutarás al utilizar CakePHP: • Comunidad activa y amistosa • Licencia flexible • Compatible con PHP4 y PHP5 • CRUD integrado para la interacción con la base de datos • Soporte de aplicación [scaffolding] • Generación de código . JavaScript o XML—. sin pérdida de flexibilidad. Las diferentes funciones del framework utilizan varias tecnologías -por ejemplo. además de no tener que reinventar la rueda. Nuestro principal objetivo es que puedas trabajar de forma estructurada y rápida.1 ¿Qué es CakePHP y por qué hay que utilizarlo? CakePHP es un marco de desarrollo [framework] rápido para PHP. Con CakePHP el desarrollo web ya no es monótono porque ofrecemos las herramientas para que empieces a escribir el código que realmente necesitas: la lógica específica de tu aplicación. el núcleo de tu aplicación se mejora constantemente y está bien probado. sino cómo se utilizan en este contexto. 1. # El Bakery . videos. Sesión y Manejo de solicitudes • Listas de control de acceso flexibles • Limpieza de datos • Caché flexible • Localización • Funciona en cualquier subdirectorio del sitio web.cakephp. Las respuestas pueden demorarse. con URLs y rutas personalizadas y limpias • Validación integrada • Plantillas rápidas y flexibles (sintaxis de PHP. para reducir nuestra carga de trabajo.http://www. Como ocurre con muchos otros proyectos de código abierto. tenemos gente nueva con regularidad. Cuando estés familiarizado con CakePHP. con ayudantes[helpers]) • Ayudantes para AJAX.org Este manual (y la API) es probablemente el primer lugar al que tienes que dirigirte para obtener ayuda.org El sitio web oficial de CakePHP es siempre un gran lugar para visitar. por lo que. y descargas. oportunidades de donar. . # El Cookbook . Cookie.cakephp. Tanto el manual como la API tienen una versión en línea.cakephp. accede a él y comparte tus conocimientos con la comunidad.2 Dónde obtener ayuda # El sitio oficial CakePHP .• Arquitectura Modelo Vista Controlador (MVC) • Despachador de peticiones [dispatcher].org La Panadería de CakePHP (CakePHP Bakery) es un almacén de cosas relacionadas con CakePHP. Consúltalo si estás buscando tutoriales. casos de estudio o ejemplos de código. intenta primero buscar respuestas por tí mismo.http://bakery. Cuenta con enlaces a herramientas de desarrollo frecuentemente utilizadas. formularios HTML y más • Componentes de Email.http://book. Javascript. con poca o ninguna configuración de Apache 1. Seguridad. pero una vez obtenidas las recordarás durante mucho tiempo. preguntas frecuentes.cakephp. Nos gustaría mucho saber de tí: si necesitas ayuda. CakePHP tiene un grupo Google muy activo. Estos te pueden servir de ejemplos prácticos para el uso de funciones y datos miembros de una clase.http://api. un recurso excelente en que se encuentran respuestas archivadas.org CakeForge es otro recurso para desarrolladores que puedes utilizar para hospedar tus proyectos CakePHP para compartir con los demás.2. echa un vistazo a CakeForge.http://api.org/tests Si crees que la información que proporciona la API no es suficiente. Los casos de prueba se encuentran en: cake/tests/cases # El canal IRC • #cakephp -. Si estás buscando (o quieres compartir) un componente interesante o un plugin recomendable. • #cakephp-es -.org/ Directo al grano y directamente de los desarrolladores de CakePHP. # Los Casos de Prueba .Documentación #cakephp-bakery -. Generalmente. Se trata de una guía de referencia de código concisa.Bakery Si te quedas estancado en algún punto. si deseas encontrar usuarios en tu área o si quieres donar tu flamante coche deportivo.# El API . # CakeForge . y donde también puedes obtener respuestas inmediatas a tus problemas.cakeforge. . sobre todo durante las horas de luz solar de América del Norte y América del Sur. algún miembro del equipo de desarrollo suele estar conectado. la API (Interfaz de Programación de Aplicaciones) CakePHP es la documentación más completa y directa que explica los detalles internos del funcionamiento del framework.Discusión General • • #cakephp-docs -.cakephp. consulta el código de los casos de prueba que proporciona CakePHP 1.http://www.Canal de la comunidad hispano-parlante # El Google Group Además. consúltanos en el canal IRC de CakePHP. Para obtener casos de prueba necesitas bajar un paquete nightly o hacer un checkout de un branch de svn. la vista hace una presentación del modelo de datos. . 2.ejemplo. Ricardo hace clic en el enlace apuntando a http://www. Programar utilizando MVC consiste en separar la aplicación en tres partes principales. y el controlador maneja y enruta las peticiones [requests] hechas por los usuarios. puede comprobar si Ricardo ha iniciado sesión.http://groups. y su navegador hace una petición al servidor web. El controlador realiza lógica de aplicación específica. supongamos que un usuario llamado Ricardo acaba de hacer clic en el enlace "¡Comprar un pastel personalizado ahora!" de la página de inicial de la aplicación.3 Entendiendo Modelo-Vista-Controlador Las aplicaciones CakePHP bien escritas siguen el patrón de diseño de software MVC (ModeloVista-Controlador). El modelo representa los datos de la aplicación.com/group/cakephp-esp/ 1.google. suscríbete al grupo de la comunidad hispanohablante. http://groups. A efectos ilustrativos.com/group/cake-php/ Si estás buscando un grupo Google en español. y le pasa la petición al controlador adecuado. Por ejemplo. El despachador comprueba la URL de la petición (/pasteles/comprar).google. 3. Figura 1: Una petición MVC básica La figura 1 muestra un ejemplo sencillo de una petición [request] MVC en CakePHP.com/pasteles/comprar. 1. una vez construyas tu primera aplicación con CakePHP. así que. así como realizar rápidamente el prototipado. 5. Separar las funciones de la aplicación en modelos. pero una vista puede ser fácilmente un PDF.4. o un objeto JSON. el controlador utiliza un modelo para buscar la última compra de Ricardo en la base de datos. aunque también pueden representar entradas LDAP. . o ficheros en el sistema. Estas características nuevas se añaden fácilmente y las antiguas toman automáticamente una forma nueva. La mayoría de las veces las vistas en CakePHP vienen en formato HTML. de forma modular y mantenible. 6. vamos a completar algunos detalles específicos de Cake. El diseño modular permite a los diseñadores y a los desarrolladores trabajar conjuntamente. Más adelante. se los pasa a la vista. Una vez que el controlador ha hecho su magia en los datos. La mayoría de las veces los modelos representan tablas de una base de datos. por favor. un documento XML. Una vez que el objeto encargado de procesar vistas en CakePHP ha utilizado los datos del controlador para construir una vista completa. La vista toma los datos y los deja listos para su presentación al usuario. dependiendo de tus necesidades. canales RSS. En este ejemplo. ten esto en cuenta a medida que avanzamos. el contenido se devuelve al navegador de Ricardo. El controlador también utiliza modelos para acceder a los datos de la aplicación. estamos seguros de que. # Beneficios ¿Por qué utilizar MVC? Porque es un patrón de diseño de software probado y se sabe que funciona. vistas y controladores hace que la aplicación sea muy ligera. no querrás volver a hacerlo de otra forma. Casi todas las peticiones a tu aplicación seguirán este patrón básico. Con MVC la aplicación se puede desarrollar rápidamente. Esta separación también permite hacer cambios en una parte de la aplicación sin que las demás se vean afectadas. Aunque lleva algún tiempo acostumbrarse a construir aplicaciones así. Además. En lugar de escribir lógica en el método de un controlador. Comportamientos [Behaviors]. desde los nombres de los archivos hasta los de las tablas de la base de datos. Los Callbacks disponibles incluyen: • beforeFilter(). ve preparando los detalles acerca de cómo usar estas herramientas. Puedes utilizar estos callbacks si necesitas insertar alguna lógica en las operaciones del núcleo de CakePHP.2 Principios básicos de CakePHP El framework CakePHP proporciona una base robusta para tu aplicación. Puede manejar cualquier aspecto. agregan rápidamente funcionalidad a las clases base MVC de las aplicaciones. se ejecuta después de la lógica del controlador. y Ayudantes [Helpers] son clases que proporcionan extensibilidad y reusabilidad. manteniendo toda tu aplicación consistente y lógica. un componente suele ser una buena elección. Este aspecto es simple pero poderoso. Como de momento nos vamos a mantener en este nivel de dificultad. se ejecuta antes que cualquier otra acción del controlador • beforeRender(). A modo de ejemplo.1 Extensiones de los Controladores ("Componentes") Un componente es una clase que ayuda a la lógica de un controlador.1. 2. puedes empaquetarla en un componente para poder compartirla. como el framework sigue los principios MVC. El framework también proporciona una estructura de organización básica. Modelo [Model] y Vista [View].1 Estructura de CakePHP CakePHP incluye las clases Controlador [Controller]. 2. pero antes de que la vista se renderice . puedes fácilmente personalizar y extender muchos aspectos de tu aplicación. Los Controladores también están equipados con callbacks. Los Componentes [Components]. Si tienes alguna lógica y la quieres compartir entre varios controladores (o aplicaciones). pero también incluye otras clases y objetos que hacen que el desarrollo en MVC sea un poco más rápido y agradable. Sigue las convenciones y siempre sabrás exactamente dónde están las cosas y cómo están organizadas. la clase del núcleo EmailComponent hace que la creación y el envío de mensajes de correo electrónico sea tan sencillo como coser y cantar. desde la solicitud inicial del usuario hasta el renderizado final de la página web. Puede que no haya ninguna diferencia entre afterRender() y afterFilter().1. La mayoría de las aplicaciones repiten piezas de código en sus vistas. CakePHP facilita la reutilización de este código con diseños [layouts] y elementos [elements]. los DataSources le permiten decirle a su modelo LDAP que está asociado a muchos eventos iCal. 2. los elementos entran en juego cuando hay que reutilizar estos fragmentos pequeños de contenido. el AjaxHelper.1. incluido el renderizado de la vista. Por defecto. puede especificar que su modelo de usuario se comporte como un árbol. toda vista renderizada por un controlador se coloca en un diseño [layout]. y obtener libre funcionalidad para eliminar. Si bien la principal fuente de datos en una aplicación CakePHP es a menudo una base de datos.2 Extensiones de las Vistas Un ayudante [Helper] es una clase que ayuda a la lógica de una vista. el manejo de las peticiones Ajax en las vistas es mucho más fácil. los ayudantes [helpers] hacen que varias vistas accedan y compartan lógica presentacional. o eventos iCal.3 Extensiones de los Modelos Del mismo modo. Por ejemplo. Con uno de los ayudantes del núcleo. los modelos también incluyen callbacks: • beforeFind() • afterFind() • beforeValidate() . Los modelos también cuentan con el apoyo de otra clase llamada DataSource (Origen de datos). añadir.• afterFilter(). a menos que hayas llamado manualmente a render() en el controlador y hayas incluido alguna lógica después de esa llamada. Así como los controladores. los Comportamientos [Behaviors] son formas de añadir funcionalidad común entre los modelos. y mover nodos en la estructura de árbol subyacente. puede escribir DataSources adicionales que le permitan a sus modelos representar canales RSS. Del mismo modo que varios controladores utilizan un componente. 2. entradas LDAP. se ejecuta después de toda la lógica del controlador. Los DataSources son una abstracción que permite a los modelos manipular diferentes tipos de datos en forma consistente. archivos CSV. Los DataSources le permiten asociar registros de diferentes fuentes: en lugar de limitarse sólo a uniones [joins] SQL. si almacena datos de los usuarios en una estructura de árbol. Las rutas juegan un rol en las peticiones hechas a CakePHP. $var2). Asegúrese de obtener los detalles en el capítulo acerca de los modelos. imaginemos que nuestro amigo Ricardo acaba de hacer clic en el enlace "¡Comprar un pastel personalizado ahora!" en una página de bienvenida de una aplicación CakePHP. ayudantes [helpers] o modelos. AppHelper (localizado en /app/app_helper.1. El comportamiento por defecto asume que la URL "/controller/action/var1/var2/" mapea a Controller::action($var1. 2.php).php) y AppModel (localizado en /app/app_model. Continuando con nuestro ejemplo de petición original. pero puede usar rutas para personalizar URLs y la forma en que éstas son interpretadas por su aplicación.• beforeSave() • afterSave() • beforeDelete() • afterDelete() Los nombres de estos métodos deben ser lo suficientemente descriptivos para que sepa lo que hacen. Algunas características en una aplicación merecen ser empaquetadas como un todo. 2. Un plugin es un paquete de modelos. así que echemos un vistazo a cómo los objetos trabajan juntos para completar una petición básica. Las definiciones de rutas le dicen a CakePHP cómo mapear URLs a acciones de controladores. AppController (localizado en /app/app_controller.php) son magníficos lugares donde colocar métodos que desee compartir entre todos los controladores. . controladores y vistas que cumplen un propósito específico que puede abarcar múltiples aplicaciones.2 Una petición típica de CakePHP Hemos cubierto los ingredientes básicos de CakePHP.4 Extensiones de la Aplicación Tanto los controladores como los ayudantes [helpers] y modelos tienen una clase padre que puede usarse para definir cambios a nivel global de la aplicación. Un sistema de administración de usuarios o un blog simplificado pueden ser buenos ejemplos para plugins de CakePHP. . el controlador usa un modelo para obtener información de la base de datos de las últimas compras de Ricardo. Negro = elemento requerido. es el método comprar() del controlador PastelesController.com/tortas/comprar. El callback beforeFilter() del controlador es llamado antes de que cualquier acción lógica del controlador sea ejecutada.Figura 2. En este caso. • Usando las rutas. comportamiento [behavior]. En este ejemplo. Petición típica de Cake. y cualquier otro argumento(s) que pueda afectar a la lógica de negocio durante esta petición. una petición URL es mapeada a una acción de controlador (un método en una clase de controlador específica). • El enrutador analiza la URL para extraer los parámetros para esta petición: el controlador. Mientras que el uso del modelo no es requerido. Gris = elemento opcional. todos los controladores de CakePHP inicialmente .ejemplo. y orígenes de datos [DataSources] aplicables pueden activarse durante esta operación. Azul = callback • Ricardo hace clic en el enlace apuntando a http://www. la acción. y su navegador hace una petición a su servidor Web. Cualquier callback de modelo. • El controlador puede usar modelos para ganar acceso a los datos de la aplicación. • Finalmente. ésta es devuelta al controlador. La lógica de vista es ejecutada. Los callbacks de controlador pueden ser aplicados antes de que la información sea enviada. • Una vez que el controlador ha usado modelos y componentes para preparar suficientemente la información. • Después que el modelo ha obtenido toda la información. • La carpeta cake es donde nosotros hemos hecho nuestra magia. por ejemplo). Por defecto. Comprométete a no modificar los ficheros de esta carpeta. o envíos de email. autenticación. la cual puede incluir el uso de elementos y/o ayudantes [helpers]. 2. • El controlador puede usar componentes para refinar aun más los datos o realizar otras operaciones (manipulación de sesiones. salvo que el desarrollador indique lo contrario.php • README Observarás 3 carpetas principales: • La carpeta app será donde haremos nuestra magia: es donde se ubicarán los ficheros de tu aplicación. El código completo creado por la vista es enviado al navegador de Ricardo. estos serán los ficheros y carpetas que deberías ver: • app • cake • vendors • . Pueden activarse callbacks del modelo. .htaccess • index.3 Estructura de archivos de CakePHP Tras descargar y extraer CakePHP. • Callbacks del controlador adicionales (como afterFilter) pueden ser aplicados. ésta es entregada a la vista usando el método set() del controlador. la vista es creada dentro del diseño [layout]. la carpeta vendors es donde ubicarás las librerías PHP de terceros que necesites usar con tus aplicaciones en CakePHP. No podremos ayudarte si has modificado el núcleo.requieren al menos un modelo. ya que también existe una carpeta vendors en el nivel superior de nuestra estructura de directorios. archivos de configuración del núcleo y demás deberían ser almacenados aquí. ya que de lo contrario el rendimiento de tu aplicación se verá muy afectado. Asegúrate de que esta carpeta existe y tiene permisos de escritura. arranque (bootstrapping). La información que realmente se almacena depende de cómo hayas configurado CakePHP. Veámos un poco más de cerca las carpetas dentro de app. Contiene los controladores de tu aplicación y sus componentes. esta carpeta debería servir como la raíz webroot del sitio (document root) para tu aplicación. Contiene los paquetes de plugins. En una configuración de producción.# La Carpeta App La carpeta app de CakePHP es donde realizarás la mayor parte del desarrollo de tu aplicación. Detalles de config controllers locale models plugins conexión a bases de datos. imágenes y archivos JavaScript. Hacerlo así hace que sea más fácil de acceder a ellas usando la función App::Import('vendor'.'nombre'). En modo debug CakePHP te avisará si este no es el caso. Los observadores meticulosos vendors notarán que esto parece redundante. Contiene los (pocos) archivos de configuración que usa CakePHP. Contiene los modelos de tu aplicación. Almacena archivos de cadenas de texto para la internacionalización. views Los archivos de presentación son ubicados aquí: elementos (elements). ayudantes (helpers). Las carpetas aquí también sirven como lugares de almacenamiento para hojas de estilo en cascada (CSS stylesheets). Veremos las diferencias entre las dos cuando discutamos acerca de la administración de múltiples aplicaciones y configuraciones de sistemas más complejos. Aquí es donde CakePHP almacena datos temporales. páginas de error. tmp registros (logs) y algunas veces información de sesiones. Cualesquiera clases o librerías de terceros deberían ser ubicadas aquí. pero normalmente esta carpeta es usada para almacenar descripciones de modelos. . comportamientos (behaviors) y orígenes de datos (datasources). layouts y archivos de vistas. esto es especialmente útil cuando se trabaja con sistemas heredados.2.4. Sin embargo. Persona. La clase EmailComponent es encontrada en un archivo llamado email.php.4 Convenciones de CakePHP Somos grandes fanáticos de convención sobre configuración. Los nombres de las tablas correspondientes a modelos de CakePHP están en plural y usando guión bajo. personas_grandes. Los nombres de los campos con dos o más palabras se definen con guiones bajos: nombre_y_apellidos. Puedes utilizar la librería de utilidades "Inflector" para verificar las palabras singulares/plurales. deberíamos mencionar que muchos de estos postulados pueden ser anulados. . por ejemplo. y PersonaMuyGrande son todos ejemplos de nombres de modelos convencionales. y la clase HtmlHelper es encontrada en un archivo llamado html.php. y también se libera de la pesadilla del mantenimiento del seguimiento de los archivos de configuración. los nombres de archivo llevan el símbolo underscore "_". y personas_muy_grandes respectivamente. Mientras que le sugerimos el uso de estas convenciones durante el desarrollo con CakePHP. usted obtiene libre funcionalidad. PersonaGrande.php. usted ahorrará tiempo en la marcha: siguiendo las convenciones. Consulta la documentación de Inflector para más información. 2.1 Convenciones de los nombres de archivos y clases En general. Aun cuando toma un poco de tiempo aprender las convenciones de CakePHP. el nombre de la clase que contiene un archivo puede no necesariamente ser encontrada en el nombre de archivo.4. permitiendo a otros desarrolladores ayudar más fácilmente.2 Convenciones de Modelo y de la Base de datos Los nombres de las clases de modelos están en singular y en formato CamelCase. La clase MyNiftyClass puede ser encontrada en el archivo my_nifty_class. Las convenciones también hacen un sistema de desarrollo muy uniforme. mientras que los nombres de las clases usan CamelCase. Las tablas subyacentes para los modelos arriba mencionados serían: personas. Las convenciones de CakePHP han sido destiladas de años de experiencia de desarrollo web y mejores prácticas. 2. Todas las tablas con las que interaccionan los modelos de CakePHP (con excepción de las de unión de tablas) necesitan una clave primaria simple que identifique inequívocamente cada fila. y Terminan en Controller. tag_id INT(10) NOT NULL.El nombre por defecto de las claves foráneas en relaciones hasMany. Si deseas modelar una tabla que no tiene una clave primaria de un sólo campo. usadas en relaciones hasAndBelongToMany (HABTM).4. Para una tabla compuesta por varias palabras como tipos_categorias. PRIMARY KEY(id) ). 2. con formato CamelCased. la convención de CakePHP dicta que se añada una clave primaria de un solo campo a la tabla. la clave foránea sería tipo_categoria_id. Así. PersonasController y UltimosArticulosController son ejemplos de nombres convencionales de controladores. usa llamadas directas a query o añade una clave primaria para que actue como un modelo normal. Por ejemplo: CREATE TABLE posts_tags ( id INT(10) NOT NULL AUTO_INCREMENT. El primer método que escribas para un controlador debe de ser el método index(). CakePHP no soporta claves primarias compuestas. En vez de utilizar una clave autoincremental como clave primaria. puedes utilizar char(36). De este modo CakePHP utilizará un uuid(String::uuid) único de 36 caracteres siempre que grabes un nuevo registro utilizando el método Model::save. si Panadero hasMany Tarta. el comportamiento por defecto de CakePHP es . es el nombre de la tabla relacionada (en singular) seguido de _id. El nombre de las tablas de unión entre modelos. post_id INT(10) NOT NULL. como por ejemplo las filas de una tabla de unión posts_tags. Si deseas manipular directamente los datos de tu tabla de unión. belongsTo o hasOne. la tabla tartas referenciará la tabla panaderos mediante la clave foránea panadero_id. Cuando la petición especifica un controlador pero no una acción. debería estar formado por el nombre de las tablas que une puestos en orden alfabético (cebras_manzanas en vez de manzanas_cebras).3 Convenciones de Controladores Los nombres de las clases de los controladores son en plural. example. una petición de http://www.com/noticias/_buscaNuevosArticulos/ obtendrá un error porque el nombre del método está precedido por un guión bajo.php') y accedido desde http://example. También puedes cambiar la visibilidad de los métodos de los controladores en CakePHP anteponiendo al nombre del método guiones bajos.4. Por ejemplo. ApplesController (que se define en el archivo 'apples_controller.com/apples/view se corresponde con una llamada al método view() del controlador ApplesController. } } ?> Mientras que la página http://www. mientras que http://www.example.1 Consideraciones de URL para nombres de controladores Como se puede ver. Por ejemplo: <?php class NoticiasController extends AppController { function ultimas() { $this->_buscaNuevosArticulos(). Si un método de un controllador comienza por un guión bajo. 2.com/noticias/ultimas/ está accesible de manera normal.com/apples/ se corresponde con la llama al método index() del controlador ApplesController. } function _buscaNuevosArticulos() { //Lógica para encontrar los nuevos articulos. Por ejemplo. sino que estará disponible sólo para uso interno. Por otro lado múltiples combinaciones de palabras pueden ser transformadas automáticamente en un mismo nombre de controlador: • • • • /redApples /RedApples /Red_apples /red_apples .ejecutar el método index() de dicho controlador. los controladores con un nombre simple (de una sola palabra) pueden ser fácilmente mapeados a una url en minúsculas. el método no será accesible diretamente desde la web.3.example.example.com/apples. si alguien intenta acceder a la página http://www. El patrón básico es: /app/views/controller/underscored_function_name. Ninguna de estas relaciones han sido configuradas por otra razon que crear clases y archivos que usted necesita crear. encontrada en /app/models/person. puede revisar el tutorial para realizar un Blog en CakePHP. y se renderiza en el archivo.Todas resuelven la acción index de controlador RedApples. Para mas información sobre CakePHP URLs y sus parametros. .ctp por ejemplo. RedApplesController::go_pick.php Plantilla de Vista.php Clase de Controlador: ‘PeopleController’. PeopleController. la convención es que las urls sean en minúsculas y separadas con guión bajo.com/people/ apunta a la llamada de funcion index() en el controlador . que se encuentra al final de este manual. por lo tanto /red_apples/go_pick es la forma correcta de acceder a la acción. donde el modelo Person esta disponible automaticamente (y apunta automaticamente a la tabla ‘people’ en la base de datos).4. CakePHP entiende que la peticion http://example. Aquí encontramos un ejemplo final de las convenciones • • • • Tabla de Base de Datos: ‘people’ Clase de Modelo: ‘Person’. encontrada en /app/controllers/people_controller. La funcion getReady() del controlador PeopleController se visualizara con la plantilla de vista en /app/views/people/get_ready. 2.4 Convenciones de Vistas Los archivos de plantillas de Vistas (Views) deben ser nombradas después de las funciones de los controladores con guión bajo "_". usted adquiere funcionalidad sin mucho mantenimiento de la configuración. encontrada en /app/views/people/index. Ahora que usted ya se ha involucrado con los fundamentos de CakePHP.ctp Usando estas convenciones. ver Configuración de Rutas. sin embargo.ctp Al nombrar las piezas de su aplicación utilizando las convenciones de CakePHP. 3. PHP 4. pero nos imaginamos que la mayoría de las aplicaciones usarán uno. ¡solo eso! Aunque este manual se enfoca primariamente en la configuración sobre Apache (por que es el usado comúnmente). Preparar la instalación consta de los siguientes pasos: • • • Descargue CakePHP Configure su servidor para manejar php si es necesario Chequee los permisos de los archivos . a cocinar. Tu puedes configurar Cake para correr sobre la mayoría de servidores web. Por ejemplo: Apache.3 Desarrollando con CakePHP Y ahora. tales como.2 o superior. pero no requerido.Los requisitos mínimos son un servidor web y una copia de Cake. Preferiblemente con mod_rewrite. Si.3. LightHTTPD o bien Microsoft IIS.1 Requerimientos • • Servidor HTTP. Técnicamente no se requiere un motor de base de datos.2 Preparándose para Instalar CakePHP es rápido y fácil de instalar. CakePHP funciona genial en PHP 4 y 5. CakePHP soporta una gran variedad de motores de almacenamiento: • • • • • • • • MySQL (4 o superior) PostgreSQL Firebird DB2 Microsoft SQL Server Oracle SQLite ODBC ADOdb 3. y es menos seguro. • Producción: Requiere la capacidad de configurar el servidor web para definir el "document root". Las releases de CakePHP estásn disponibles en http://cakeforge. producción y avanzado • Desarrollo: fácil para iniciar.2. 3.3 Instalación Instalar CakePHP puede ser tan simple como colocar el directorio en el servidor. Estas pueden ser accedidas desde la página principal de descargas aquí: http://cakephp. Por ello.1 Obteniendo CakePHP Hay dos principales maneras de obtener una copia limpia de CakePHP. Para descargar la última release principal de CakePHP. o puedes obtener el código desde el repositorio git. Esta sección cubrirá los tres tipos principales de instalación para CakePHP: desarrollo. entre otros. Para actualizaciones realmente al minuto. puedes obtener el código directamente de la rama de desarrollo del repositorio git aquí: http://code.org/downloads/index/nightly. asegúrate que el directorio /app/tmp de tu instalación de Cake tenga permisos de escritura por el usuario del servidor web 3. Puedes descargar una copia comprimida (zip/tar. Todas las releases actuales están alojadas en CakeForge. dirígete a la página web http://www. Este site también contiene enlaces a muchos otros proyectos en CakePHP. información de sesiones. Se crean nightly builds alternativas que incluyen parches y mejoras al minuto (bueno.org/source.bz2) de la página web principal. incluyendo plugins y aplicaciones para CakePHP. al día). o tan complejo y flexible como necesites.org y haz clic en el enlace “Download Now”.org/projects/cakephp. los URL de la aplicación incluyen el nombre del directorio. permite ubicar los directorios clave de CakePHP en diferentes partes del sistema de archivos.3. muy seguro. como almacenar descripciones de los modelos.2 Permisos CakePHP usa el directorio /app/tmp para diferentes operaciones. • Avanzado: Con cierta configuración.cakephp.gz/tar.2.cakephp. vistas en cache. para compartir una misma instalación de CakePHP . Tu configuración de desarrollo será como la siguiente en el sistema de archivos: • /var/www/html • cake_1_2 • /app • /cake • /vendors • .ejemplo. Descomprime los contenidos del archivo Cake en /var/www/html. Este ejemplo te ayudará a installar Cake en cualquier sitio de tu sistema de ficheros y ponerlo disponible en http://www.0. Por motivos de ejemplo.php • README Si tu servidor web está configurado correctamente.ejemplo. Renombra esta carpeta a cake_1_2.ej. 3.2 Producción Una instalación de producción es una manera más flexible de instalar Cake. asumimos que escoges instalar Cake en /cake_install.3.htaccess • /index. 3. Tu configuración de producción se verá de la siguiente manera en el sistema de ficheros: • /cake_install/ . Descomprime los contenidos del archivo Cake en un directorio a tu elección.7962).2.com/cake_1_2/.para varias aplicaciones. Este ejemplo te ayudará a instalar una aplicación de CakePHP y hacerla disponible en http://www. Ahora tienes una carpeta en tu raíz de documentos con un nombre dependiente de la versión que te has descargado (p. deberías encontrar tu aplicación de Cake accesible en http://www. Asumimos para el fin de este ejemplo que tu raíz de documentos está establecido a /var/www/html.com.3. cake_1.com/cake_1_2/.ejemplo. Tener en cuenta que esta instalación puede requerir los privilegios para cambiar el DocumentRoot (raíz de documentos) en servidores web Apache. Usar este método permite que un dominio entero se comporte como una aplicación CakePHP única.1 Desarrollo Usar una instalación de desarrollo es el método más rápido para montar Cake. en /app. Esto puede ser debido a restricciones de un servidor compartido.ejemplo. o quizás simplemente deseas que algunas de tus aplicaciones compartan las mismas librerías de Cake. Para configurar tu instalación de Cake. deberías encontrar tu aplicación Cake accesible en http://www. 2. • ROOT debe contener la ruta del directorio que contiene la carpeta app (es decir. Esta sección describe cómo esparcir los directorios de CakePHP sobre un sistema de archivos.• /app • /webroot (este directorio es el establecido con la directiva DocumentRoot) • /cake • /vendors • /. . date cuenta que existen tres partes principales de una aplicación Cake: 1. el padre de APP_DIR). en /cake.htaccess • /index. 3. a excepción del webroot.php • /README Los desarrolladores que usan Apache deberán establecer la directiva DocumentRoot para el dominio a: DocumentRoot /cake_install/app/webroot Si tu servidor web está configurado correctamente.3 Instalación Avanzada Existen situaciones en las que querrás colocar los directorios de CakePHP en lugares diferentes del sistema de archivos.php. normalmente en /app/webroot. El código de tu aplicación. que debe ser accesible por el servidor web. En primer lugar. Incluso puedes mover el directorio webroot fuera de tu carpeta app siempre que le indiques a Cake en donde lo has puesto. Cada uno de estos directorios puede ser ubicado en cualquier lugar de tu sistema de archivos. APP_DIR. Las librerías principales(core) de CakePHP. Hay tres constantes que deberás editar: ROOT.3.com. El webroot de la aplicación. y CAKE_CORE_INCLUDE_PATH. 3. necesitarás hacer algunos cambios a /app/webroot/index. 'home'. Dada esta configuración.php (codigo parcial.'usr'. 'misitio'). El webroot de mi aplicación será /var/www/misitio/. necesitaré editar mi webroot/index. } if (!defined('APP_DIR')) { define ('APP_DIR'.1 Rutas de Clase Adicionales Ocasionalmente es útil compartir clases MVC entre aplicaciones en el mismo sistema. CAKE_CORE_INCLUDE_PATH debe contener la ruta al directorio que contiene las librerías de Cake.3. En el archivo bootstrap. en este ejemplo) para que sea así: // /app/webroot/index.'lib').3. . puedes usar el archivo bootstrap. define algunas variables especiales para que CakePHP sepa otros lugares en donde buscar clases MVC: $viewPaths = array(). 3. sin comentarios) if (!defined('ROOT')) { define('ROOT'.'yo'). } if (!defined('CAKE_CORE_INCLUDE_PATH')) { define('CAKE_CORE_INCLUDE_PATH'. Vamos a mostrar un ejemplo para ver cómo quedaría una instalación avanzada en la práctica.php.php de CakePHP para traer estas clases adicionales a la escena.DS. DS. Esto evita errores por falta de archivo como resultado de usar el delimitador equivocado.DS. } Es recomendable utilizar la constante DS en lugar de barras inclinadas para delimitar las rutas de los archivos.php (el cual terminará ubicado en /var/www/misitio/index. DS. Imagina que quiero configurar CakePHP para que funcione de esta manera: • • • Las Librerías de CakePHP serán ubicadas en /usr/lib/cake.• • APP_DIR debe ser establecida con el nombre(base) de tu carpeta app.php. y además hace tu código más portable. Si quieres el mismo controler en dos aplicaciones. El directorio app de mi aplicación estará en /home/yo/misitio. Debes ver algo como: LoadModule rewrite_module libexec/apache2/mod_rewrite.3.htaccess esté permitida y que AllowOverride esté establecido a All para el DocumentRoot adecuado. array(). array(). configuramos "por defecto" para que sea un conjunto de # características muy restrivo. # <Directory /> Options FollowSymLinks AllowOverride All # Order deny. Primero mira en tu httpd. y normalmente así es. Cada una de estas variables especiales pude ser establecida a un array de rutas absolutas en el sistema de archivos donde las clases adicionales pueden ser encontradas cuando se solicite. 3. Asegúrate que la reescritura .conf específico de un usuario o del site). 1. array(). .conf (asegúrate de estar editando el httpd. array().conf del sistema y que no es httpd.allow # Deny from all </Directory> 2. hemos notado que algunos usuarios tienen dificultades para lograr que todo funcione correctamente en sus sistemas. array(). Aquí hay unas cuantas cosas que puedes probar para conseguir que funcione correctamente. array(). En muchos sistemas esto estará comentado (comenzando la línea con #) por defecto. # # Primero. así que sólo tendrás que quitar los símbolos # del principio.htaccess) A pesar de que CakePHP está hecho para funcionar con mod_rewrite sin tocar nada. Deberías ver algo similar a: # Cada directorio al que tiene acceso Apache puede ser configurado en # función de qué servicios y características están permitidas y/o # desactivadas en dicho directorio (y sus subdirectorios). array().4 Apache y mod_rewrite (y . array(). Asegúrate que cada ruta contenga una barra inclinada (o preferiblemente la constante DS) al final. Asegúrate de estar cargando el módulo mod_rewrite correctamente.$controllerPaths $modelPaths $helperPaths $componentPaths $behaviorPaths $pluginPaths $vendorPaths $localePaths $shellPaths = = = = = = = = = array(). ' como ocultos y por lo tanto no los copian. por ejmplo. o en cualquier otra estructura que ya utilice mod_rewrite necesitarás añadir sentencias RewriteBase a los archivos . Asegúrate de que tu copia de CakePHP es de las sección de descargas de nuestro site o nuestro repositorio GIT. tu servidor web está realmente siendo servido desde un directorio de usuario que ya utiliza mod_rewrite. .*) app/webroot/$1 [L] </IfModule> En el directorio app de Cake (será copiado por bake): <IfModule mod_rewrite. y que ha sido desempaquetado correctamente verificando que existen los ficheros .php?url=$1 [QSA.htaccess) Esto puede ser añadido a la misma sección con la directiva RewriteEngine.*) webroot/$1 </IfModule> [L] [L] En el directorio webroot de Cake (será copiado a tu webroot de la aplicación por bake): <IfModule mod_rewrite. 1and1).htaccess. 3. así. Si estás instalando CakePHP en un directorio de usuario (http://ejemplo.htaccess están en los directorios correctos.htaccess que utiliza CakePHP (/. /app/.htaccess: En el directorio raíz de Cake (necesita ser copiado al directorio.L] </IfModule> En muchos servicios de hosting (GoDaddy. /app/webroot/.c> RewriteEngine on RewriteRule ^$ app/webroot/ [L] RewriteRule (.Tras realizar los cambios reinicia Apache para estar seguro de que las opciones de configuración están activas.c> RewriteEngine on RewriteRule ^$ webroot/ RewriteRule (.htaccess. Esto puede pasar durante la copia porque algunos sistemas operativos consideran los archivos que comienzan por '. esto redirige todo a tu aplicación de Cake): <IfModule mod_rewrite.*)$ index.com/~nombreusuario/cakephp). Asegúrate de que tus ficheros .c> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(. CakePHP.htaccess en el webroot devería ser así: <IfModule mod_rewrite. -. prefix) return str:sub(1.php?url=$1 [QSA. no es equivalente al mod_rewrite de Apache. mayoritariamente necesita mod_magnet para redirigir las solicitudes a fin de trabajar con bastantes URLs Para utilizar bastantes URLs con CakePHP y Lighttp. Las funcinalidades completas de mod_rewrite se reparten entre el mod_rewrite de Lighttp.L] </IfModule> <IfModule mod_rewrite. Sin embargo.tu archivo . el mod_magnet y el mod_proxy..c> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index. Consulta la documentación online de Apache para más información.3.5 Lighttpd y mod_magne Aunque Lighttpd cuenta con un módulo de reescritura.php?url=$1 [QSA.#prefix+1) == prefix.c> RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(. y pueden incluir cosas adicionales que no están relacionadas con Cake. sustituye este lua script en /etc/lighttpd/cake.stat(path) if (attr) then return true else return false end end function removePrefix(str."/" and str:sub(#prefix+2) end .pequeña funcion helper function file_exists(path) local attr = lighty. 3.*)$ index.L] </IfModule> Los detalles de esos cambios dependen de tu configuración. Dependiendo de tu configuración.ejemplo.sh". "/index. (uriquery ~= "" and "&" or "") . ".3. "url=" .) Then tell Lighttpd about your vhost: $HTTP["host"] =~ "example. ".env["uri. pasarlo al fastcgi Backend request_uri = removePrefix(lighty.tpl.fallthrough pondrá de nuevo la solucititud en el bucle lighty -..-.eso significa que tenemos la manipulación 304 de forma gratuita. ".uri"] lighty. "Root".env["physical.env["physical.path"])) then -. "empty" ) } 3.env["uri.ctp". lighty.php" magnet. necesitarás modificar esto.orig-uri"] = lighty.doc-root"] . consume menos recursos del sistema.php" local uriquery = lighty.xtmpl". rewrite ^(.query"] or "" lighty.env["physical. ".rel-path"] end end -.env["uri.path"].attract-physical-path-to = ( "/etc/lighttpd/cake. ".path"] = lighty. .la magia .htaccess como Apache y Lighttpd. server_name www. Su inconveniente es que no utiliza ficheros . del mismo modo que Lighttpd.env["request.error-handler-404 = "/index.env["request. "Entries".2/app/webroot/" # además piensa como coger los ficheros vim tmp fuera url.document-root = "/var/www/cake-1.com" { server.6 Pretty URLs en nginx nginx es un servidor popular que. "sql".access-deny = ( "~".path"] = prefix .env["physical.inc".lua" ) server. .sql"... pero como mínimo necesitarás que PHP se ejecute como instancia de FastCGI.*) http://ejemplo. request_uri lighty.com$1 permanent.env["physical.env["uri.env["uri.) if (not file_exists(lighty.prefijo sin la barra local prefix = '' -. "...php". "Repository".fichero aún desaparecido. prefix) if request_uri then lighty. por lo que es necesario crear esas reescrituras de URLs en la configuración de site-available.rel-path"] = lighty.com.query"] = uriquery . server { listen 80.path"] lighty. .log.+)$ /index.webServer> <rewrite> <rules> <rule name="Imported Rule 1" stopProcessing="true"> <match url="^(.config 3.com. To do this. Use Microsoft's Web Platform Installer to install the URL Rewrite Module 2.3. follow these steps: 1.conf.html index. location / { root /var/www/ejemplo. copy the following code into your new web. } if (-d $request_filename) { break.php. fastcgi_index index. } } 3. Create a new file in your CakePHP folder. fastcgi_param SCRIPT_FILENAME /var/www/ejemplo. fastcgi_pass 127.0. you can also import htaccess rules into IIS to use CakePHP's native rewrites.*\. } location ~ .com/public/app/webroot/.0.} server { listen 80. 2. called web. } rewrite ^(. Using Notepad or another XML-safe editor. server_name ejemplo.com/public/app/webroot$fastcgi_script_name. error_log /var/www/ejemplo. index index. if (-f $request_filename) { break.config file.0.log.php index.com/log/error.htaccess files..0" encoding="UTF-8"?> <configuration> <system. access_log /var/www/ejemplo.php?q=$1 last.1:10005.php[345]?$ { include /etc/nginx/fcgi. While there are add-ons that can add this support.com/log/access. <?xml version="1.*)$" ignoreCase="false" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" .htm.7 URL Rewrites on IIS7 (Windows hosts) IIS7 does not natively support . ¡Felicidades! Ya estás listo para crear tu primera aplicación CakePHP.htaccess files in root.php?url={R:1}" appendQueryString="true" /> </rule> <rule name="Imported Rule 2" stopProcessing="true"> <match url="^$" ignoreCase="false" /> <action type="Rewrite" url="/" /> </rule> <rule name="Imported Rule 3" stopProcessing="true"> <match url="(.php?url={R:1}" appendQueryString="true" /> </rule> </rules> </rewrite> </system. Dependiendo de la configuración que hayas usado. En este punto. and /app/webroot/ . ahora veamos a CakePHP en acción. When Importing the rules this way.*)$" ignoreCase="false" /> <conditions logicalGrouping="MatchAll"> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> </conditions> <action type="Rewrite" url="index.*)" ignoreCase="false" /> <action type="Rewrite" url="/{R:1}" /> </rule> <rule name="Imported Rule 4" stopProcessing="true"> <match url="^(.although some editing within IIS may be necessary to get these to work. js.negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> </conditions> <action type="Rewrite" url="index.config file for you. . /app/.webServer> </configuration> It is also possible to use the Import functionality in IIS's URL Rewrite module to import rules directly from CakePHP's .8 Enciéndelo Muy bien. se te presentará la vista de bienvenida de CakePHP por omisión. and rerouting should work correctly.com/mi_aplicacion/. css. IIS will automatically create your web.config file is created with the correct IIS-friendly rewrite rules.com/ o http://example.3. y un mensaje que indica el estado de conexión con la base de datos. 3. deberías apuntar tu navegador a http://example. CakePHP's links. Once the web. si mi aplicación tiene aplicaciones legadas adicionales a la que se va a utilizar por defecto. Si se debe usar o no una conexión persistente a la base de datos. otras configuraciones opcionales. pear-nombrededriver. Esta configuración debería verse como sigue: var $default = array('driver' 'persistent' 'host' 'login' 'password' 'database' 'prefix' ). => 'mysql'. en los modelos correspondientes. Ejemplo: mysql. podría utilizarla en mis modelos creando un nuevo arreglo de configuración $legada que sea similar a $default. 'mi_proyecto'.1 Configuración de Base de Datos CakePHP espera que los detalles de configuración de la base de datos estén en app/config/database. sqlite. adodb-nombrededriver. oracle. => => => => => => false. como mejor se ajuste a tus necesidades Clave driver persistent host login password Valor El nombre del controlador de base de datos que se desea utilizar. Tu puedes agregar facilmente elementos al núcleo de CakePHP. Un ejemplo de configuración puede encontrarse en el archivo app/config/database. Por ejemplo. mssql. 'localhost'.3. El nombre de servidor de la base de datos (o dirección IP).4. Rellena cada para clave/valor en el arreglo de configuración. 'c4k3roxx!'. Existen. . '' El arreglo de configuración $default es el utilizado a menos que se especifique algún otro en la propiedad $usDbConfig de los modelos. que puedes realizar con el objetivo de aprovechar las ventajas de la arquitectura flexible de CakePHP. 'cakephpuser'. y asignado var $useDbConfig = 'legada'. 3.php. configurar URL personalizadas y definir inflexiones. Despues de instalar CakePHP.default. El nombre de usuario para la cuenta. crear una aplicación web básica solo requiere aplicar la configuración de una base de datos. odbc.4 Configuración Configurar aplicaciones CakePHP es pan comido.php. La contraseña para la cuenta. postgres. Usado en la configuración de PostgreSQL para especificar el esquema a utilizar. esta debe llamarse prefijo_sabor_torta (no prefijo_sabor_prefijo_torta). El puerto TCP o socket Unix a usarse para la conexión con el servidor de base de datos. minúsculas. y nombres en plural para los nombres de tus tablas . si nombras una tabla como tortas. necesitarás familiarizarte con Configure. Hacerlo de esa manera te obligaría a incluir estos archivos cada vez que desees utilizarlos. Este archivo es una colección de definiciones de variables Configure y definiciones de constantes que determinan como ha de comportarse la aplicación. esta clase permite almacenar cualquier cosa en ella. Útil si se comparte la base de datos con varias aplicaciones.3 La Clase de Configuración A pesar de que muy pocas cosas necesitan configuración en CakePHP. todo funcionará automáticamente si necesidad de tu intervención. no para los modelos. para luego usarla en cualquier lugar de tu código: una tentación segura para . Se cuidadoso. La nueva clase Configure de CakePHP puede ser utilizada para guardar y recuperar valores específicos de la aplicación o de tiempo de ejecución. pudieras querer echar un vistazo a las Convenciones de CakePHP.4. la clase de registro de configuraciones de CakePHP 3.php. Dejar vacío si no se desea ninguno. utiliza guiones bajos. reposterias. a veces es útil tener tus propias reglas de configuración para tu aplicación. Por ejemplo. A este punto.2 Configuración del Core La configuración de la aplicación en CakePHP se encuentra en /app/config/core.por ejemplo: reposteros. Indica la codificación de caracteres a usar para enviar las sentencias SQL al servidor. Por ejemplo. Por convención. el modelo Torta y el controller TortasController. Nota que los prefijos son para las tablas. y asignar la clave prefix con ‘prefijo_’.database prefix (opcional) port (opcional) encoding schema Nombre de la base de datos a usar para la conexión El texto que prefija cada nombre de tabla en la base de datos. si creaste una tabla join para tus modelos Torta y Sabor. En el pasado seguramente habrías definido alguno de estos valores creando constantes en varios archivos. sabores. 3. Antes de sumergirse en cada una de estas directivas. Nombrar correctamente tus tablas (y algunas columnas) según las convenciones puede librarte de mucho trabajo de configuración. enunciadas en el apéndice de este manual.4. nombre'. Si se especifica una clave.1 Métodos de la Clase Configure 3. $int) para cambiar entre desarrollo y producción dentro de la ejecución de tu programa.'Pizza. Configure::write('Empresa. Puedes utilizar esta notación para organizar tus configuraciones dentro de grupos lógicos.3.array('nombre'=>'Pizza. y sus métodos pueden ser llamados desde cualquier lugar en tu aplicación.romper el patrón MVC con el cual fue diseñado CakePHP. Recuerda tratar de mantener la filosofía de "convención sobre configuración" y así no terminarás rompiendo la estructura MVC que se ha diseñado.3. <?php Configure::read('debug').4.'.1.'lema'=>'Pizza para tu cuerpo y alma') ). Nota el uso de la notación punto en el parámetro $clave. 3. ?> 3.'). en un contexto estático.2 read read(string $clave = 'debug') Se usa para leer datos de configuración de la aplicación.lema'. Esto es especialmente útil para interacciones AMF o SOAP donde la información de depuración puede ocasionar problema de parseo. Puedes utilizar Configure::write(‘debug’.1.4. Inc.1 write write(string $clave. Por defecto devuelve el importante valor de "debug" (nivel de depuración). mixed $valor) Utiliza write() para almacenar datos en la configuración de la aplicación Configure::write('Empresa. Esta clase actúa como un singletón. El ejemplo anterior pudo ser escrito en una sola llamada: Configure::write( 'Empresa'. Inc.'Pizza para tu cuerpo y alma'). los datos correspondientes .3.4. El objetivo principal de la clase Configure es mantener centralizadas las variables que pueden ser compartidas entre varios objetos. //devuelve: 'Pizza.4 load load(string $path) Usa este método para cargar información de configuración desde una archivo específico.1.nombre').3.3 delete delete(string $clave) Se usa para borrar información de configuración de la aplicación.nombre'). ?> 3. //devuelve: array('nombre' => 'Pizza.3. 3.4. Configure::read('Empresa').2 Variables de Configuración Principales de CakePHP La clase Configure se usa para manejar un conjunto de variables de configuración de CakePHP.' //devuelve: 'Pizza para tu cuerpo y alma' 3. Abajo se encuentra una descripción de .php: <?php $config['Empresa']['nombre'] = 'Pizza.4.1. Inc.'. Configure::read('Empresa.4. 'lema' => 'Pizza para tu cuerpo y alma').nombre'). // /app/config/mensajes. Usando nuestros anteriores ejemplos de write().son devueltos.lema').1.php.5 version version() Devuelve la versión de CakePHP de la aplicación actual. podemos leer esos datos de vuelta: Configure::read('Empresa. ?> <?php Configure::load('mensajes').3.'. $config['Empresa']['lema'] = 'Pizza para tu cuerpo y alma'. Estas variables pueden ser encontradas en app/config/core. Inc. $config['Empresa']['telefono'] = '555-55-55'. 3. Configure::read('Empresa. Inc. Configure::delete('Empresa.4.3. El nombre del cookie utilizado para hacer seguimiento de las sesiones. Security.save php = Utiliza el almacenamiento por defecto de php cake = Guarda los datos de sesión en /app/tmp database = Guarda los datos en una tabla de la base de datos. No te olvides de eliminar los archivos . Si se asigna true.cada variable y cómo afecta tu aplicación CakePHP. Descomenta esta definición si deseas utilizar las rutas admin de CakePHP.salt Acl.level Inicia automáticamente la sesión cuando se asigna true. El nombre de la base de datos que guarda los datos de sesión. El tiempo base de validez de la sesión en segundos. pero esta variable habilita la detección de dichas configuraciones.classname.checkAgent Security. Le indica a CakePHP qué mecanismo de almacenamiento de sesiones se debe utilizar Session. El tiempo de validez de la sesión definido en 'Session. Lee el capítulo de listas de control de acceso para más información. el cache se deshabilita para toda la aplicación. El nombre de la tabla (sin incluir el prefijo) que guarda los datos de la sesión.timeout Session.admin Cache.cookie Session. También es necesario activar el cache en los controllers. Más adelante se explicará en detalle. 1 = Muestra los error y warnings. Variables usadas para las Listas de Control de Acceso de CakePHP.baseUrl Routing. Cuando se asigna false.level Valores válidos: 'high' = x 10 'medium' = x 100 'low' = x 300 Una palabra aleatoria usada en sumas de seguridad.htaccess también. warnings. Asigna la variable al nombre de la ruta que te gustaría utilizar. Asegúrate de cargar el archivo SQL ubicado en /app/config/sql/sessions. habilita el cache de las vistas.database . Acl.database Session.table Session. El valor real depende de la variable Security. App. 2 = Muestra los error.timeout' se multiplica de acuerdo a lo siguiente. No produce ninguna salida.sql. Las sesiones de CakePHP no se asegurarán de que el "user agent" del usuario no cambie entre peticiones. El nivel de seguridad de CakePHP. Variable de Configuración Descripción Cambia el nivel de depuración de cake debug 0 = Modo produción.disable Cache. Cuando se asigna true.start Session. y consultas SQL Descomenta esta definición si no deseas utilizar el mod_rewrite de Apache.check Session. Esto puede ser especialmente útil si desea deshabilitar el nivel de deburacion ("debug") para una sección limita de lógica en tu aplicación.4.php — Más adelante cubriremos este tema. Constante LOG_ERROR Descripción Constante de error.4.3. Lo anterior hará que la clase Sanitize esté disponible para su uso. A primera vista App::import parece complejo. existen unas pocas constantes que utiliza CakePHP durante su ejecución.Nota: La configuración de Cache también puede ser encontrada en el archivo core. 3. Actualmente PHP solo soporta LOG_DEBUG.1 Usando App::import() App::import($type. Usada para diferenciar entre registros de depuración y registros de error.4 La Clase App Cargar clases adicionales se ha vuelto mucho más sencillo con CakePHP. Estas funciones han sido reemplazadas. 'Sanitize'). sin embargo.3 Constantes de Configuración A pesar de que la mayoría de las opciones de configuración se manejan con la clase Configure. $return). Éste método te asegura que una clase ha sido cargada sólo una vez. 3. En versiones anteriores existían funciones diferentes para cargar una clase dependiendo de su tipo. en la mayoría de los casos es suficiente con tan sólo dos parámetros.2 Importando librerías del Core Las librerías del Core. 3.4. que las clases que extiende se hayan cargado apropiadamente. $name. La clase Configure puede ser utilizada para leer y escribir valores durante la ejecución del programa.4. ahora toda la carga de clases debería hacerse a través de el método App::import().4. como Sanitize y Xml pueden ser cargadas mediante: App::import('Core'. $parent. . y resuelve las rutas de ubicación automáticamente en la gran mayoría de los casos. $search.4. 3. $file. 4.4. 'Html').4.5 Cargando Ayudantes[Helpers] App::import('Helper'.3.Comentario'). // If we want the model associations. App::import('Controller'.3 Importando Controladores.4. App::import('Modelo'.3. 3.4. Es importante darse cuenta que la clase posteriormente necesita ser inicializada.4.4.4. components. 'MyController').3.4 Cargando Comportamientos [Behaviors] App::import('Behavior'. 3.4.3. Llamando a App::import es equivalente a require. Ayudantes. 3.4. 'MyModel').4.4. 'NombrePlugin. etc to be loaded $Users->constructClasses(). // Necesitamos cargar la clase $Users = new UsersController. Comportamientos y Componentes App::import('Controller'.php'). . 3. a excepción de que debe especificarse el nombre del plugin donde reside la clase a cargar. ?> 3. Modelos. 'Tree').3. 'Users'). 'Auth').3 Cargando Componentes [Components] App::import('Component'. <?php // Lo mismo que require('controllers/users_controller.2 Cargando Modelos [Model] App::import('Model'.4 Cargando desde Plugins Cargar clases en plugins funciona casi igual que cargar clases ubicadas en el Core o en la aplicación principal. nombre.3. y más flexibles. 3. pero hará lucir mucho mejor tu barra de direcciones. 'CiertoNombre'.php')). 'geshi').4. Usar el mod_rewrite de Apache no es un requisito para utilizar el enrutamiento. array('file' => 'cierto.DS.named.php App::import('Vendor'. Enrutamiento por Defecto Antes de que aprendas a configurar tus propias rutas.5 Configuración de Rutas El enrutamanieto permite hacer una relación entre URLs y acciones de los controller. Los siguientes ejemplos ilustran cómo cargar archivos de terceros en diferentes rutas y estructuras.php App::import('Vendor'. deberías saber que CakePHP viene configurado con un conjunto de rutas por defecto. La sintaxis y los argumentos adicionales son levemente diferentes. Puedes acceder a una acción directamente desde el URL colocando su nombre en la .php')).4. Para cargar vendors/cierto.4. Para cargar vendors/flickr/flickr.named.php App::import('Vendor'. 3. debido a que los archivos de terceros y su estructura pueden variar inmensamente. Los archivos de terceros deben estar ubicados en cualquiera de los directorios vendor.nombre. Estas rutas te llevarán bastante lejos en cualquier aplicación. Los archivos de terceros deben ser cargados también mediante App::import(). Para cargar vendors/services/well. Las rutas en CakePHP 1. Fue añadido a CakePHP para hacer más bonitos los URLs. 'WellNamed'.5 Cargando Archivos de Terceros La función vendor() ha sido reemplazada.5.'well.2 han sido mejoradas y pueden llegar a ser muy poderosas.4.4.php App::import('Vendor'. más configurables. 'flickr/flickr').1 Ejemplos de archivos de terceros Para cargar vendors/geshi. array('file' => 'services'. y no todos los archivos de terceros contienen clases. php usando el método Router::connect(). se asume que se trata de la acción index(). También puedes pasar parámetros a las acciones de tus controladores usando el URL. Una petición para /articulos/ver/titulo:primer+articulo/categoria:general resultaría en una llmada a la accion view() de ArticulosController.com/controlador/accion/param1/param2/param3 El URL /articulos/ver dirige a la acciónver() action del ArticulosController. Tus rutas deben ser definidas en el archivo /app/config/routes. La configuración inicial de enrutamiento permite pasar parámetros a tus acciones usando el URL. '2001'). Puedes nombrar parámetros y enviar sus valores usando el URL. Parámetros con Nombre Algo novedoso en CakePHP 1. Definir Rutas Definir tus propias rutas te permite definir cómo va a responder tu aplicación cuando se solicite un URL determinado. Patron URL de las rutas por defecto: http://example.petición. En dicha acción. por ejemplo. . puedes encontrar los valores del título y la categoría dentro de $this->passedArgs['titulo'] and $this->passedArgs['categoria'] respectivamente. Si no se especifica ninguna acción en el URL. $this->passedArgs['seccion'] = 'asociaciones'. URL: /tareas/ver/45 Dirige a: TareasController->ver(45).2 es la habilidad de usar parámetros con nombre (named parameters). URL: /donaciones/ver/recientes/2001 Dirige a: DonacionesController->ver('recientes'. Algunos ejemplos que resuman las rutas por defecto puede resultar útil. URL: /contenidos/ver/capitulo:modelos/seccion:asociaciones Dirige a: ContenidosController->ver(). y /productos/listaCompleta dirige a la accion to the lista_completa() de ProductosController. $this->passedArgs['capitulo'] = 'modelos'. URL: /monos/saltar Dirige a: MonosController->saltar(). Una petición para /articulos/ver/25 sería equivalente a llamar ver(25) en el ArticulosController. URL: /productos Dirige a: ProductosController->index(). 'action' => 'mostrar'. Las claves de este arreglo deben ser nombradas como los elementos de enrutamiento en el URL. El URL es una cadena de caracteres separadas por barras inclinadas (slash). :action y :plugin. :controller. Esta ruta se encuentra en el archivo routes. array('nombreParam' => 'valorPorDefecto'). . El segundo parámetro es una arreglo asociativo. o los elementos por defecto. Router::connect( '/mujeres'. y especificar elementos de enrutamiento te permite recolectar parámetros para las acciones de tus controladores Una vez que hayas especificado un URL.php distribuido con CakePHP (linea 40). y expresiones regulares que ayuden al enrutador a hacer coincidir elementos en el URL. 5) ). 'action' => 'display') ). que son.El método connect() toma hasta tres parámetros: el patron de URL que deseas hacer coindicir. array('controller' => 'productos'. por ejemplo. Esta ruta coincide con los URL que comienzan con /pages/ y delega a la acción display() de PagesController el manejo de la petición. los valores por defecto para los elementos de enrutamient propios. array('nombreParam' => 'expresionRegular') ) El primer parámetro es usado para decirle al enrutador qué tipo de URL estás tratando de controlar. pero también pueden contener el un comodín comodín (*) o elementos de enrutamiento propios (Elementos de URL prefijados con el caracter dos puntos ":"). Usar un comodín le indica al enrutador qué tipos de URL quieres hacer coincidir. Veamos algunos ehjemplos básicos antes de empezar a usar el tercer parámetro de connect() Router::connect( '/pages/*'. La petición /pages/productos puede sería dirigida a PagesController>display('productos'). Los valores en este arreglo son los valores por omisión para cada una de las claves. debes utilizar los últimos dos parámetros del método connect() para decirle a CakePHP que hacer con esa petición una vez que ha sido seleccionada la regla adecuada. El formáto básico para una definición de ruta es: Router::connect( 'URL'. array('controller' => 'pages'. params del controlador. Router::connect( '/:controller/:id'. :controller e :id. array('action' => 'ver'). Cuando defines un elemento propio de enrutamiento. mientras que los elementos propios de enrutamiento se encuentran en $this->params. Ambos harán una llamada al método ver() de ManzanasController. Esto te ayuda a crear el enlace /mujeres en lugar de /productos/mostrar/5 Para tener flexibilidad adicional. array('id' => '[0-9]+') ). array( 'year' => '[12][0-9]{3}'. El URL suministrado a connect() especifica dos elementos de enrutamiento. Esto le indica a CakePHP cómo reconocer si el URL está bien formado o no. sería lo mismo que solicitar /manzanas/ver/5. El elemento :id es propio. así que el enrutador sabe cómo reconocer nombres de controladores en el URL. Este ejemplo sencillo muestra cómo crear una manera sencilla de ver registros desde cualquier controlador accediendo a un URL que luce como /mincontrolador/id. y esta es la diferencia: los parmámetros con nombre (/controlador/accion/nombre:valor) se encuentran en $this->passedArgs. Cuando se hace una petición. también necesitas especificar una expresión regular.Este segundo ejemplo muestra como usar el segundo parámetro de connect() para definir parámetros por omisión. los valores para estos elementos propios se encuentran en $this-gt. y debe ser clarificado especificando una expresión regular en el tercer parámetro de conenct(). 'month' => '(0[1-9]|1[012])'. como el nombre de una acción. array('action' => 'listar'. puedes considerar el hacer una ruta. Un ejemplo más y serás un profesional del enrutador. Una vez que hayas definido esta ruta. podrías acceder al id en $this->params['id']. Hacer esto te da el poder de definir lugares en el URL donde los parámentros para las acciones deben residir. Si construiste un sitio que muestra productos para diferentes categorías de clientes. Estos son diferentres de los parametros con nombre. Dentro del método ver(). Esto le dice a CakePHP cómo reconocer el ID en el URL en contraposición a cualquier otra cosa que esté allí. 'day' => '(0[1-9]|[12][0-9]|3[01])' . puedes especificar elementos de enrutamiento propios. El primer elemento es uno que viene por defecto con CakePHP. Router::connect( '/:controller/:year/:month/:day'. 'day' => null). solicitar /manzanas/5. simplemente agrega el tercer parámetro de Router::connect(). pero muestra lo poderosas que pueden ser las rutas. Finalmente especificamos algunas expresiones regulares que coindidiran con años. /escritos/2004/11/16 y /productos/2001/05 (¿recuerdas que el parametro day era opcional?). que es el cuarto elemento en el URL) a null.. Sin importar el controlador. $slug = null) { // mi codigo aqui. y que desea acceder a los argumentos usando $articuloID en lugar de $this->params['id']. array( // el orden importa. Puede parecer un poco enredado. puesto que esto enviará ":id" como el parámetro $articuloID de tu acción.php Router::connect( // E. gracias a las capacidades de enrutamiento inverso. array('controller' => 'blog'. enviando la petición a listar() de sus respectivos controladores. // some_controller. 'action' => 'ver'). esta ruta coindcidirá con /articulos/2007/02/01. queremos que la acción listar() sea llamada. 'pass' => array('id'. } // routes. Una vez definda. 'slug').g. Pasando parámetros a la acción Asumiendo que tu acción fue definida como se muestra a continuación.ctp . para marcarlo como opcional. Asignamos el parámetro day (día. // ver. con los parámetros de fecha definidos en $this->params. puedes usar el arreglo de url que se muestra a continuación y Cake sabrá cómo formar el URL tal como fue definido en las rutas. El primero ya debe resultarte familiar: el elemento que le dice a CakePHP que se trata de un nombre de controlador. meses y días en su forma numérica.php function ver($articuloID = null.) ). /blog/3-CakePHP_Rocks '/blog/:id-:slug'. A continuación. El URL suministrado tiene cuatro elemento.. vamos a especificar algunos valores por defecto. 'id' => '[0-9]+' ) ) Y ahora. echo $html->link('Editar tu perfil'.admin. Cualquier llamada a la sección de perfiles buscará el prefijo perfiles_ en las llamadas a métodos. lo cual invocaría el método perfiles_editar del UsuariosController. En tu controlador. 'action' => 'view'. Router::connect('/perfiles/:controller/:action'. 'admin'). array('prefix' => 'perfiles')). 'slug' => Inflector::slug('CakePHP Rocks') )) ?> Rutas con prefijos Muchas cplicaciones requieren una zona administrativa donde los usuarios privilegiados puedan hacer cambios.php asignando la ruta para Routing. .admin'. 'id' => 3. En CakePHP. también es importante reordar que usar el Ayudante HTML para construir tus enlaces te ayudará a mantener los prefijos. Configure::write('Routing. el nombre de la acción de nuestro UsuariosController debe ser admin_editar Puedes usar el enrutador para usar prefijos propios para usos más allá de la administración. Estos es comunment hecho a través de un URL especial como /admin/usuarios/editar/5.// Esto va a devolver un lik a /blog/3-CakePHP_Rocks <?php echo $html->link('CakePHP Rocks'. cualquier acción que empiece por admin_ será llamada. Este es un ejemplo de un enlace usando el ayudante HTML . Usando nuestro ejemplo de usuarios. array('controller' => 'usuarios'. 'action' => 'perfiles_editar')). Puedes utilizar múltiples prefijos de ruta para crar una muy flexible estructura de URL para tu aplicación. array( 'controller' => 'blog'. las rutas admin pueden ser habilitadas en el archivo core. Nuestro ejemplo de usuarios tendría una estructura de URL que luciría como /perfiles/usuarios/editar/5. El enrutamiento en CakePHP te ofrece mucho más que cualquier otra aplicación. deberías saber que CakePHP incluye algunas por defecto. Puedes enviar también variables a la acción a través de la URL. 3.1 Enrutamiento por defecto Antes de que leas como configurar tus rutas. El enrutamiento por defecto te permite enviar parámetros a la acción a través de la URL. Patrones de enrutamiento por defecto: http://example. '2001'). Si la acción no esta definida en la URL.2 Passed arguments Una nueva característica en CakePHP 1. En esta acción. URL: /products Enrutado: ProductsController->index().4.com/controller/action/param1/param2/param3 La URL /posts/view enruta hacia la accion view() del controlador PostsController y /products/viewClearance enruta hacia la accion view_clearance() del controlador ProductsController.2 es la posibilidad de setear nombres de parámetros y su valor por la URL. Una petición hacia /posts/view/25 sería equivalente a llamar a la acción view(25) en el controlador PostsController.5. URL: /contents/view/chapter:models/section:associations Enrutado: ContentsController->view(). Puedes acceder directamente a cualquier acción poniéndola solo en la URL.4. Acceder a la acción jump() del controlador MonkeysController desde la URL: URL: /monkeys/jump Enrutado: MonkeysController->jump(). Una petición a /posts/view/title:first+post/category:general resultaría en una llamada a la acción view() del controlador PostsController.5. el método index() es asumido por defecto. Algunos ejemplos que pueden ser de utilidad. . podrás encontrar los valores para title y category dentro de $this->passedArgs['title'] y $this->passedArgs['category'] respectivamente.3. $this->passedArgs['chapter'] = 'models'. URL: /tasks/view/45 Enrutado: TasksController->view(45). URL: /donations/view/recent/2001 Enrutado: DonationsController->view('recent'. :action. The URL is a normal slash delimited string. Using a wildcard tells the router what sorts of URLs you want to match. array('controller' => 'pages'. This route is found in the routes. The keys of the array should be named after the route elements in the URL. array('paramName' => 'matchingRegex') ) The first parameter is used to tell the router what sort of URL you're trying to control.php file distributed with CakePHP (line 40).$this->passedArgs['section'] = 'associations'. Once you've specified a URL. . and specifying route elements allows you to gather parameters for your controller actions.4. you use the last two parameters of connect() to tell CakePHP what to do with a request once it has been matched. array('paramName' => 'defaultValue'). Define your own routes in the /app/config/routes. Let's look at some basic examples before we start using the third parameter of connect(). but can also contain a wildcard (*) or route elements (variable names prefixed with a colon). Router::connect( '/pages/*'. 3. 'action' => 'display') ). The basic format for a route definition is: Router::connect( 'URL'. The request /pages/products would be mapped to PagesController->display('products'). The values in the array are the default values for those keys.php file using the Router::connect() method. or the default elements: :controller. for example. and regular expression rules to help the router match elements in the URL.5. and :plugin. This route matches any URL starting with /pages/ and hands it to the display() method of the PagesController(). The second parameter is an associative array.4 Defining Routes Defining your own routes allows you to define how your application will respond to a given URL. The connect() method takes up to three parameters: the URL you wish to match. the default values for your route elements. Router::connect( '/cooks/:action/*'. This is different than named parameters are handled. 'action' => 'someAction'. The following route easily takes care of that: Router::connect( ). Let's say that instead of accessing our regular URL at /users/someAction/5. routes are used too. Another common use for the Router is to define an "alias" for a controller. '/cooks/:action/*'. 'action' => 'index') This is telling the Router that any url beginning with /cooks/ should be sent to the users controller. So if you want the above route to match urls like /cooks/someAction/type:chef we do: Router::connectNamed(array('type')). If you are planning to use custom named arguments with your route. array('controller' => 'users'. doing so gives you the power to define places in the URL where parameters for controller actions should lie. 'action' => 'display'.Router::connect( '/government'. we'd like to be able to access it by /cooks/someAction/5. array('controller' => 'users'. 5) ). array('controller' => 'users'. This allows you link to /government rather than /products/display/5. Using array('controller' => 'users'. Router::connect( 2. 'action' => 'index') 3. You can specify your own route elements. ). 1. When a request is made. This second example shows how you can use the second parameter of connect() to define default parameters. array('controller' => 'products'. If you built a site that features products for different categories of customers. Router::connectNamed(array('type')). 'action' => 'index') ). so note the difference: named parameters (/controller/action/name:value) are found in $this- . '/cooks/:action/*'. 5) as a url will output /cooks/someAction/5 if the above route is the first match found. the values for these route elements are found in $this->params of the controller. you have to make the router aware of it using the Router::connectNamed function. you might consider creating a route. When generating urls. and you'll be a routing pro. The :id element is a custom route element.this tells CakePHP how to know if the URL is correctly formed or not. We set the day parameter (the fourth element in the URL) to null to flag it as being optional. One more example. requesting /apples/5 is the same as requesting /apples/view/5. When you define a custom route element. Once this route has been defined. we specify some default values. This simple example illustrates how to create a quick way to view models from any controller by crafting a URL that looks like /controllername/id. array('id' => '[0-9]+') ). Router::connect( '/:controller/:year/:month/:day'. but shows how powerful routes can really become. we want the index() action to be called. whereas custom route element data is found in $this->params. This is rather involved. Next. months and days in . This tells CakePHP how to recognize the ID in the URL as opposed to something else. The URL supplied has four route elements. so the router knows how to match and identify controller names in URLs. such as an action name. Regardless of the controller. 'day' => '0[1-9]|[12][0-9]|3[01]' ) ). The URL provided to connect() specifies two route elements: :controller and :id. you would need to access the passed ID at $this->params['id']. array('action' => 'index'. array( 'year' => '[12][0-9]{3}'. Both would call the view() method of the ApplesController. Inside the view() method. 'day' => null). 'month' => '0[1-9]|1[012]'. we specify some regular expressions that will match years. Router::connect( '/:controller/:id'. The first is familiar to us: it's a default route element that tells CakePHP to expect a controller name.>passedArgs. you also need to specify a regular expression . array('action' => 'view'). and must be further clarified by specifying a matching regular expression in the third parameter of connect(). Finally. The :controller element is a CakePHP default route element. . this route will match /articles/2007/02/01. array( // el orden es importante ya que esto va a mapear ":id" con $articleID en tu action 'pass' => array('id'. /blog/3-CakePHP_Rocks '/blog/:id-:slug'.5. Once defined. 'id' => 3. Y ahora. esto se hace con una URL especial como . gracias a la capacidad de enrutamiento inverso podrás pasar la url como se muestra abajo y Cake sabrá como formar la URL como se definió en los routers. the day parameter is optional as it has a default).php Router::connect( // E.. 3.6 Prefix Routing Muchas aplicaciones necesitan una sección administrativa donde los usuarios con privilegios puedan hacer cambios.5 Pasando parámetros a las acciones Asumiendo que tu action fue definida así y quieres acceder los argumentos usando $articleID en vez de $this->params['id']. } // routes. but not grouped with parenthesis. // some_controller. and /products/2001/05 (as defined. handing the requests to the index() actions of their respective controllers.4. 'slug'). with the date parameters in $this->params. $slug = null) { // some code here. ?> 3.g. array( 'controller' => 'blog'.4.numerical form. 'slug' => Inflector::slug('CakePHP Rocks') )). 'action' => 'view'). You can still specify alternates. // view.5. Con frecuencia.php function view($articleID = null. tan solo agrega un array extra en el 3er parámetro de Router::connect().ctp // esto devolverá un link a /blog/3-CakePHP_Rocks <?php echo $html->link('CakePHP Rocks'. 'action' => 'view'. as above. Note that parenthesis (grouping) are not supported in the regular expressions. 'id' => '[0-9]+' ) ). /posts/2004/11/16. array('controller' => 'blog'. He aquí cómo construir este enlace usando el HTML helper: echo $html->link('Edit your profile'. En CakePHP. array('prefix' => 'profiles'. array('controller' => 'pages'. Cualquier llamada a la sección Profiles buscaría el prefijo profiles_ en las llamadas a los métodos. será llamada cualquier acción con un prefijo admin_. acceder a la URL /admin/users/edit/5 debería llamar al método admin_edit de nuestro UsersController pasando 5 como primer parámetro. 3.4. Configure::write('Routing. el admin routing puede activarse dentro del archivo de configuración del core ajustando la ruta de administración para Routing.7 Rutas y plugins Las rutas a Plugins utilizan la clave plugin. array('profiles' => true. 'profiles' => true)). Es también importante recordar que usar el HTML helper para construir tus enlaces. Puedes mapear la URL /admin a tu acción admin_index del pages controller usando la ruta. te ayudará a mantener las llamadas a los prefijos.admin. 'admin'). Puedes configurar el Router usado múltiples prefijos: Router::connect('/profiles/:controller/:action/*'. Router::connect('/admin'.admin'. 'id' => 5)). 'controller' => 'users'. 'action' => 'edit'. 'admin' => true)). . Nuestro ejemplo tendría una URL como /profiles/users/edit/5 que llamaría al método profiles_edit en el UsersController.5./admin/users/edit/5. Puedes ajustar múltiples rutas con prefijos usando esta metodología para crear una estructura de URL flexible para tu aplicación. En tu controlador. 'action' => 'index'. Puedes crear enlaces que apunten a un plugin siempre que añadas la clave plugin al array de la url. Recurriendo a nuestro ejemplo de usuarios. 'title' => Inflector::slug('text to slug'. necesitas una línea extra en el archivo de configuración de rutas: Router::parseExtensions('html'.5. array('controller' => 'pages'.4. Parse is used to parse requests and match is used to handle reverse routing.4. 'action' => 'view'. si la petición activa es un plugin y quieres crear un enlace que no tiene plugin.5. array('controller' => 'pages'. Al poner plugin => null le estás diciendo al Router que quieres crear un enlace que no forma parte de un plugin. Esto le dirá al router que retire las extensiones de archivo coincidentes y que procese entonces el resto.html podrías crear tu ruta como se explica a continuación: Router::connect( '/page/:title'. 'rss'). array( 'pass' => array('title') ) ). 'action' => 'create')). . 'ext' => 'html')) 3.9 Custom Route classes Custom route classes allow you to extend and change how individual routes parse requests and handle reverse routing.echo $html->link('New todo'. puedes hacerlo como sigue.8 Extensiones de archivo Para manejar diferentes extensiones de archivo con tus rutas. A route class should extend CakeRoute and implement one or both of match() and parse(). 'action' => 'view').. array('plugin' => 'todo'. echo $html->link('New todo'. array('plugin' => null. Para crear enlaces que se mapeen a esas rutas simplemente usamos: $html->link('Link title'. 'controller' => 'todo_items'. 3. 'action' => 'profile')). Si quieres crear una url como /page/title-of-page. '-'). Por el contrario. 'controller' => 'users'. tu controlador "UsuariosController" y todo funcionará automáticamente. Igual que $pluralRules. La forma en que CakePHP sabe como atar unas cosas a otras es a través de las inflexiones de palabras entre formas en singular y plural.php $pluralRules $uninflectedPlural Descripción Este arreglo contienen las expresiones regulares para pluralizar los casos especiales. array('controller' => 'posts'. solo que este arreglo contiene las palabras que no tienen singular. Si CakePHP no es capaz de reconocer tu Reloj o Ciudad. array('routeClass' => 'SlugRoute') ). Las claves de este arreglo contienen la forma singular y los valores la forma plural. Un arreglo que contiene palabras y su respectivo plural.4. Este archivo se encuentra en /app/config/inflections. Nombras la tabla de base de datos "usuarios". tu modelo "Usuario". Cada una de ellas te permite definir a un grado muy fino el comportamiento de inflexiones de CakePHP. This route would create an instance of SlugRoute and allow you to implement custom parameter handling 3. editar el archivo de inflexiones propias es la manera de indicarle a CakePHP que existen otros casos especiales.php. sobre todo para usuarios de habla hispana. Un arreglo que contiene palabras que no han de ser modificadas para obtener su plural. como la palabra gente o dinero. solo que contiene las reglas para singularizar palabras. and loading the file containing your route before trying to use it. Igual que $uninflectedPlural. Router::connect( '/:slug'. Hay ocasiones. 'action' => 'view'). $irregularPlural $singularRules $uninflectedSingular . En este archivo encontrarás seis variables. Las claves del arreglo son los patrones y los valores los reemplazos. Variable de inflections. Este arreglo debe ser utilizado para colocar palabras que no sigan las reglas definidas en $pluralRules. Por defecto es igual que $uninflectedPlural.6 Inflexiones Propias Las convenciones de nomenclatura de CakePHP pueden ser muy buenas. en que encontrarás situaciones donde el inflector de CakePHP no funcione como lo esperas.You can use a custom route class when making a route by using the routeClass option. solo que con palabras en forma singular. .4. Este archivo es ejecutado inmediatamente después de el bootstrap propio de CakePHP. Resiste la tentación. Este archivo es ideal para un número de tareas comunes: • Definir funciones de conveniencia • Definir constantes globales • Definir rutas adicionales para modelos.Variable de inflections..php $irregularSingular Descripción Igual que $irregularPlural.php. usa el archivo de bootstrap de CakePHP que se encuentra en /app/config/bootstrap. Podrías considerar colocar cosas en la clase AppController. vistas.7 Haciendo Bootstrap de CakePHP Si tienes necesidades de configuración adicionales. que serán usados en todos tus controladores. Esta clase en poder de todos los controladores de la aplicación. controladores. 3. Sé cuidadoso de mantener el patrón de diseño MVC cuando agregues cosas al archivo bootstrap. plugins.. AppController es útil para colocar funciones que se ejecutan antes o después de eventos definidos (callbacks). puede resultar tentador colocar funciones para dar formato a texto allí para luego usarlas en controladores. Te lo agradecerás más adelante a ti mismo. .. que a su vez extiende la clase principal Controller.. Los controladores pueden tener cualquier cantidad de métodos a los que normalmente se les llama acciones. } } ?> . AppController es una sub-clase de Controller que es una clase de la biblioteca estándar de Cake.php y debe contener métodos que son compartidos entre todos los controladores de su aplicación. si estás construyendo un sitio de una pastelería. Las acciones son métodos de controladores en tu aplicación web para mostrar vistas. La clase AppController puede ser definida en /app/app_controller.. } function buscar($query) { //la lógica de la acción va aqui.php class RecetasController extends AppController { function ver($id) { //la lógica de la acción va aqui. los controladores se nombran según el modelo que manejan. Los controladores de su aplicación son sub-clases de la clase AppController de CakePHP. y así sucesivamente.5 Controladores # Introducción Un controlador (Controller) se usa para manejar la lógica de cierta sección de su aplicación. Una acción es un único método de un controlador. $receta_id) { //la lógica de la acción va aqui.. Comúnmente. los controladores (Controllers) son usados para manejar la lógica de un solo modelo (Model).php con el siguiente contenido: <?php # /app/controllers/recetas_controller. El Dispatcher de CakePHP ejecuta acciones cuando una solicitud entrante contiene en su URL el nombre de una acción del controlador. } function compartir($cliente_id. El controlador estaría ubicado en /app/controllers/recetas_controller. En CakePHP. El modelo Receta es manejado por el RecetasController. Por ejemplo.3. y se ponen siempre en plural. podrías tener un RecetasController y un IngredientesController para manejar las recetas y sus ingredientes.. el modelo Producto es manejado por el ProductosController. php como: <?php class AppController extends Controller { } ?> Las propiedades y métodos creados en tu AppController estarán disponibles para todos los controladores de tu aplicación. es generada utilizando este controlador.com/pages/about_us Cuando "cocinas" una aplicación utilizando la consola de CakePHP el controlador de páginas es copiado a tu carpeta app/controllers/ y puedes modificarla a tus necesidades si es . Cuando se aplica la herencia a los objetos. AppController extiende la clase Controller incluida en la libreria base de CakePHP. repasaremos algunos de los atributos y métodos provistos por los controladores de CakePHP. los arrays del AppControler son combinados con los arrays de la clase hijo. como una lista de componentes o ayudantes utilizados por un controlador.2 The Pages Controller El núcleo de CakePHP viene con un controlador por defecto llamado the Pages Controller (el Controlador de Páginas) (cake/libs/controller/pages_controller. recuerda realizar las llamadas a los callbacks de AppController desde los controladores de tu aplicación para que todo funcione correctamente: function beforeFilter(){ parent::beforeFilter(). 3.5.ctp puedes accesarlo utilizando la url http://example. En estos casos.5. La página de inicio que ves luego de la instalación. } 3. CakePHP también realiza un trabajo extra cuando existen atributos especiales en el controlador. la clase AppController es la clase superior a todos los controladores de tu aplicación. Es el sitio ideal para poner el código que será común a todos los controladpres de tu aplicación. Así. AppController es definida en /app/app_controller.php). Por ejemplo: Sí creas un archivo de vista app/views/pages/about_us. Los Componentes (los cuales veremos después) son lo más utilizado para el código que se utiliza en la mayoría (pero no necesariamente en todos) los controladores.1 The App Controller Como se comentó en la introducción. Por favor.Para que puedas usar un controlador de manera productiva en tu propia aplicación. 3. y nuestro ProductsController también posee el modelo Product en $this->Product. <?php # $name Ejemplo de uso del atributo $name class RecetasController extends AppController { var $name = 'Recetas'. Nuestro RecipesController tendrá disponible la clase modelo Recipe en $this->Recipe. Utilizar esos atributos hace que las clases MVC estén disponibles al controlador como variable de clase($this->ModelName.2 $components. Esto previene algunos problemas de distinción de mayúsculas que tiene PHP4 para los nombres de las clases. Form. } ?> 3. Los controladores tienen acceso a su modelo primario disponible por defecto. 3.5. Usualmente este es simplemente el plural del modelo principal al que el controlador está asociado. Los ayudantes (Helpers) Html. Para aprender más sobre estas clases. y modelos (models) utilizarás en conjunción con el controlador actual.5. Este atributo debería ser asignado con el nombre del controlador.php del núcleo a tu app.5. .3. y Session están siempre disponibles por defecto.necesario. O simplemente puedes copiar el archivo page_controller.org/class/controller. por ejemplo).3 Atributos del Controlador Para ver la lista completa de atributos visite la API de CakePHP en la sección http://api. componentes (components). como lo es SessionComponent. no olvides leer sus respectivas secciones más adelante en este manual.cakephp. $helpers y $uses Los siguientes atributos más comunmente utilizados del controlador indican a CakePHP qué ayudantes (helpers).1 $name Los usuarios de PHP4 deberían empezar la definición de sus controladores con el atributo $name.3. Veamos cómo decirle a un controlador de CakePHP que planeas utilizar clases MVC adicionales. } } ?> También puedes cambiar el título de la página (que está localizado en la barra en la parte superior de tu navegador) utilizando $pageTitle. var $helpers = array('Ajax'). default. tu diseño (layout) necesita incluir la variable $title_for_layout como mínimo entre las etiquetas <title> y </title> en la cabecera del documento HTML.3. o cualquier cosa que es declarada en tu controlador App. var $components = array('Email'). Especificas un diseño al igualar $layout al nombre del archivo con el diseño excluyendo la extensión .3 Atributos Relacionados con la Página: "$layout" y "$pageTitle" Existen unos pocos atributos en los controladores de CakePHP que te dan control sobre cómo se colocan tus vistas (views) dentro del diseño (layout). por lo tanto no es necesario (por ejemplo) declarar le ayudante (helper) Form. Si no has definido un diseño en /app/views/layouts/default. el diseño por defecto del núcleo de CakePHP’s será renderizado. Para que esto funcione apropiadamente. 'User'). var $uses = array('Recipe'.ctp.5.ctp. CakePHP renderiza el diseño por defecto. } ?> Cada una de estas variables es fusionada con sus valores heredados. <?php .<?php class RecipesController extends AppController { var $name = 'Recipes'.ctp. Si este atributo no ha sido definido. <?php // Usando <em>$layout</em> para definir un diseño alternativo class RecipesController extends AppController { function quickSave() { $this->layout = 'ajax'. Al atributo $layout se le puede asignar el nombre de un diseño (layout) guardado en /app/views/layouts. 3. 5. se recomienda. se generará automáticamente un título basado en el nombre del controlador.3.// Usando <em>$pageTitle</em> para definir el título de la página class RecipesController extends AppController { function quickSave() { $this->pageTitle = 'Mi título del motor de búsquedas optimizado'. 3. 3.4.4. El uso más común de $this->params es obtener acceso a información que ha sido entregada al controlador a través de las operaciones POST o GET. } } ?> También puedes establecer el título desde la vista (view) usando $this->pageTitle (Has de incluir la parte $this->.3.5. 0 si no.3.4 isAjax $this->params['ajax'] Almacena un 1 si la petición actual es una llamada ajax.4.5. Si $this->pageTitle no está establecido.4 El Atributo de Parámetros ("$params") Los parámetros del controlador están disponibles en $this->params en tu controlador de CakePHP. Esta variables sólo se establece si el componente RequestHandler es usado en el controlador. 0 si no. 3. Esta variables es usada para proporcionar acceso a la información sobre la petición actual.5. Para una página estática has de usar $this->pageTitle en la vista si quieres un título personalizado. . incluyendo información también hallada en $_FILES.1 form $this->params['form'] Cualquier dato POST de cualquier formulario se almacena aquí. ya que separa la lógica del diseño y el contenido). 3.2 admin $this->params['admin'] Almacena un 1 si el diseño (layout) actual está vacío.3. o el nombre del fichero de la vista en el caso de una página estática. 3.5. Por ejemplo.3.4. .3. entonces $this->params['pass'] será igual a "?var1=3&var2=4". si fue pedida la URL /posts/view/?var1=3&var2=4.5. Por ejemplo.3.6 action $this->params['action'] 3. entonces $this->params['url'] debería contener: [url] => Array ( [url] => posts/view [var1] => 3 [var2] => 4 ) 3. junto con los pares clave-valor de variables get.3.first_name'). si fue pedida la URL /posts/view/1.3. Por ejemplo.9 data $this->data Usado para manejar datos POST enviados desde los formularios de FormHelper al controlador.7 pass $this->params['pass'] Almacena la cadena de consulta GET enviada con la petición actual.4.5. // El helper FormHelper es usado para crear un elemento de formulario: $form->text('User. 3.5 controller $this->params['controller'] Almacena el nombre del controlador actual que está sirviendo la petición.8 url $this->params['url'] Almacena la URL actual pedida. si se llamó a la URL /posts/view/?var1=3&var2=4.5.4.4.5. $this->params['controller'] será igual a "posts".3.4. 5. Por ejemplo.4.4.5. los datos aparecen en this->data // El valor first_name enviado se puede encontrar aquí: $this->data['User']['first_name'].3.10 prefix $this->params['prefix'] Establecido al prefijo de enrutado.3. este atributo contendría la cadena "admin" durante una petición a /admin/posts/someaction. .3. écha un vistazo a sus respectivas secciones más adelante en este manual. Para más información sobre cómo utilizar esos atributos. hay otros atributos del controlador que merecen sus propias secciones en el manual. 3. y el atributo $paginate es usado para establecer las opciones por defecto de paginado para el controlador. entonces $this>params['named'] debería contener el array: [named] => Array ( [var1] => 3 [var2] => 4 ) 3.El cual al ser renderizado. se ve parecido a: <input name="data[User][first_name]" value="" type="text" /> Cuando el formulario es enviado al controlador mediante POST.5. Por ejemplo.11 named $this->params['named'] Almacena cualquier parámetro con nombre /clave:valor/ de la cadena de petición de la URL. 3. si se pidió la URL /posts/view/var1:3/var2:4.5 Otros Atributos Aunque puedes ojear todos los detalles para todos los atributos del controlador en el API. El atributo $cacheAction ayuda en el "cacheado" (caching) de vistas (views). todos los modelos relacionados con el controlador (Controller) se almacenan en caché.3. Cuando se coloca en verdadero (true).6 persistModel Usado para crear instancias almacenadas en caché de modelos (Models) un uso de Controlador (Controller). Esto puede incrementar el desempeño en muchos casos.5. .3. html. la variable puede ser accedida en tu vista.5.1.2/class_controller. Una vez que has utilizado set().2 render render(string $action.): <?php $data = array( 'color' => 'pink'. A menudo.5.4 Métodos del Controlador Para una lista completa de los métodos del controlador y sus descripciones visita el API de CakePHP. <?php // Primero pasas datos desde el controlador: $this->set('color'. Las claves (keys) serán flexionadas (inflected) antes de ser asignadas a la vista ('clave_con_subrayado' se convierte en 'claveConSubrayado'. El método set() también toma una array asociativo como primer parámetro. $type.5.cakephp.1 set set(string $variable. en las vista.4. puedes utilizar el dato: ?> Has seleccionado <?php echo $color. 'type' => 'sugar'. // Despueś. string $file) . 'azul').org/1.3. mixed $valor) El método set() es la principal manera de enviar datos desde tu controlador a tu vista (view). ?> 3. // hace que $color. 3. ?>ar la tarta.1 Interactuando con Vistas 3.1. 'base_price' => 23. string $layout.95 ).4. etc. y $basePrice // estén disponibles a la vista: $this->set($data). Echa un vistazo a http://api. esto puede ser una manera rápida de asignar un conjunto de información a la vista.4.5. integer $status. algo muy útil en las llamadas ajax. Cuando estés usando $file.. Por ejemplo. } A pesar de que CakePHP lo llamará automáticamente (a menos que hayas establecido $this>autoRender a falso) después de cada lógica de las acciones.ctp $this->render('/elements/ajaxreturn'). El parámetro $layout te permite especificar el diseño en el que la vista es renderizada. Si $action comienza por '/' se asume que es un fichero de vista o elemento relativo a la carpeta /app/views. Esto permite el renderizado inmediato de elementos.2 Control de Flujo 3.2. coloca la vista (view) dentro de su diseño (layout) y lo sirve de vuelta al usuario final.ctp. } . class RecipesController extends AppController { function search() { // Render the view in /views/recipes/search. no olvides utilizar unas pocas de las constantes globales de CakePHP (como VIEWS). 3.El método render() es llamado automáticamente al final de cada acción de controlador pedida. // Render the element in /views/elements/ajaxreturn. Este método . $file. si se pide la acción search() del controlador RecipesController. El fichero de vista por defecto utilizado por render es determinado por convenio.ctp $this->render().5.1 redirect redirect(string $url. boolean $exit) El método de control de flujo que más frecuentemente utilizarás es redirect().4.. Este método lleva a cabo toda la lógica de la vista (usando los datos que has proporcionado con el método set()). será renderizado el fichero de vista en /app/views/recipes/search. puedes utilizar render para especificar un fichero de vista alternativo indicando un nombre de acción en el controlador usando $action.5.4. También puedes especificar un fichero de vista alternativo usando el tercer parámetro. string $url. } } El segundo parámetro de redirect() te permite definir un código de estado HTTP que acompañe la redirección. Para mensajes flash en la página. Puede que desees usar 301 (movido permanentemente) o 303 (mirar otro). dependiendo de la naturaleza de la redirección.toma su primer parámetro en forma de URL relativa de CakePHP. probablemente desearás redirigirle a una ventana de recibo.5.2. 3. El método flash() es diferente en cuanto que muestra un mensaje antes de enviar al usuario a otra URL. El primer parámetro debería contener el mensaje a mostrar.3 Retrollamadas ("Callbacks") Los controladores de CakePHP vienen con retrollamas (callbacks) empotradas que puedes usar para insertar lógica justo antes o después de que las acciones del controlador sean llevadas a cabo. Por ejemplo. cerciónate de echarle un ojo al método setFlash() del componente SessionComponent. } else { $this->redirect(array('controller' => 'pedidos'.5. beforeFilter() Esta función se ejecuta antes de toda acción en el controlador. cuando un usuario ha hecho un pedido satisfactoriamente. CakePHP mostrará el mensaje en $message durante el número de segundos en $pause antes de reenviar al usuario a otra página. 'action' => 'confirmar')).4. Es un lugar práctico para . integer $pause) Igualmente. el método flash() es usado para redirigir un usuario a una nueva página tras una operación.4. y el segundo parámetro es una URL relativa a CakePHP. 3. 'action' => 'gracias')). El metodo ejecutará exit() tras la redirección a menos que establezcas el tercer parámetro a false. function realizarPedidos() { // La lógina para finalizar el pedido va aquí if($satisfactorio) { $this->redirect(array('controller' => 'pedidos'.2 flash flash(string $message. _afterScaffoldSave($metodo) $metodo es el nombre del método llamado tras edit o update. edit. Este callback no es utilizado a menudo. 3. _scaffoldError($metodo) $metodo es el nombre del método llamado. por ejemplo: index. afterRender() Llamada tras haber sido renderizada una acción. pero antes de que la vista es renderizada.4. _beforeScaffold($metodo) $metodo es el nombre del método llamado. pero puedes necesitarlo si estás llamando a render() manualmente antes del final de una acción dada.comprobar una sesión activa o inspeccionar los permisos del usuario. _afterScaffoldSaveError($metodo) $metodo es el nombre del método llamado tras edit o update. por ejemplo: index. etc.1 constructClasses Este método carga los modelos requeridos por el controlador.4. Si necesitas CakePHP en un script de línea de comando o algún otro uso externo.4 Otros Métodos Útiles 3. edit. beforeRender() Llamada tras la lógica de acción del controlador. pero hay que tener a mano este método cuando se accede a los controladores desde una perspectiva diferente. CakePHP también soporta callbacks relacionados con el scaffolding. constructClasses() será útil.5. .5. etc. afterFilter() Llamada tras toda acción del controlador.4. El proceso de carga es realizado por CakePHP normalmente. 3. proporciónalos usando el segundo parámetro. Puedes utilizar los ayudantes FormHelper y HtmlHelper para crear un formulario rápido basado en el modelo Pedido. Si deseas usar un operador SQL distinto entre términos.destino” => “Old Towne Bakery”).4.4. .4. mixed $operadores. boolean $exclusivo) Usa este método para convertir un conjunto de datos de modelo recibidor mediante POST (de inputs compatibles con HtmlHelper) en un conjunto de condiciones de búsqueda para un modelo.4. postConditions convierte esa condición en un array compatible para ser usado en un método Model->findAll().2 referer Devuelve la URL remitente de la petición actual.5. string $bool. 'LIKE') )). Entonces. un usuario administrativo puede querer buscar pedidos para saber qué elementos necesitan ser enviados. 'referrer' => 'Ye Olde' ) ) */ //Obtengamos los pedidos que tiene como mínimo 4 elementos y contienen ‘Ye Olde’ $o = $this->Pedido->findAll($this->postConditions( $this->data.4. array('>='.5. En este caso. $this->set('pedidos'. Ver referer en la wikipedia para más información. } Si $this->data[‘Pedido’][‘destino’] es igual a “Old Towne Bakery”. array(“pedido. $o). Por ejemplo.4. /* contenidos de $this->data array( 'Pedido' => array( 'num_items' => '4'.3.4 postConditions postConditions(array $datos. una acción de un controlador puede usar los datos recibidos desde ese formulario para encauzar las condiciones de búsqueda: function index() { $o = $this->Pedidos->findAll($this->postConditions($this->data)).5. Esta función ofrece un atajo rápido para la construcción de la lógica de búqueda.3 disableCache 3. el operador >= es el que se le aplica.php class CommentsController extends AppController { function latest() { return $this->Comment->find('all'.5. Primero necesitamos crear una función en un controlador que devolverá los datos. 3.5. requestAction puede llevar a un pobre rendimiento. 'limit' => 10) ).4. añádelos al array $options. // controllers/comments_controller.6 requestAction requestAction(string $url.5 paginate Este método es usado para paginar resultados cargados por tus modelos. La dirección $url pasada es una URL relativa de CakePHP (/nombrecontrolador/nombreaccion/parametros). Finalmente. Puedes usar requestAction() para obtener una vista completamente renderizada pasando 'return' en las opciones: requestAction($url. Si se utiliza sin caché. Usemos el ejemplo de poner un elemento "últimos comentarios" en el diseño (layout). Puedes especificar tamaño de páginas. ‘OR’ y ‘XOR’ son valores válidos.4. Es mejor usar requestAction junto con elementos en caché. array('return')).4.created DESC'. 3. Una cadena de carateres como ‘AND’.4.La clave al especificar operadores es el orden de las columnas en el array $this->data. y el parámetro $operadores es un array. array $opciones) Esta función llama a una acción de un controlador de cualquier lugar y devuelve los datos de la acción. si el último parámetro se establece a true. condiciones de búsqueda del modelo y más. Para pasar datos extras a la acción del controladores receptor. array( 'order' => 'Comment. Mira la sección paginación para más detalles sobre cómo usar paginate. Es ráramente apropiado usarlo en un controlador o modelo. } . los campos no incluidos en $operadores no se incluirán en las condiciones devueltas. Dado que num_items está de primero. como una manera de obtener datos para un elemento antes de renderizar. El tercer parámetro te permite decirle a CakePHP qué operador booleano SQL usar entre condiciones de búsqueda. requestAction ahora toma urls con estilo cake basadas en arrays: echo $this->requestAction( array( 'controller' => 'articles'. 'action' => 'featured' ). con una diferencia. siempre que el elemento sea renderizado. es mejor utilizar caché de elementos para prevenir procesamiento innecesario. Escrito de esta manera. } Podemos colocar esos elementos en cualquier sitio para obtener la salida usando: echo $this->element('latest_comments'). Esto es porque requestAction sólo combina los argumentos nombrados del array en el array de miembros de Controller::params y no coloca los argumentos con nombre en la clave 'named'. Además. La llamada a requestAction no se realizará mientras que la el archivo de la vista del elemento en cache exista y sea válido.ctp $comments = $this->requestAction('/comments/latest'). de acuerdo con el aviso anterior. echo $this->requestAction('/articles/featured/limit:3'). Las urls basadas en arrays son las mismas que las que HtmlHelper:link usa. foreach($comments as $comment) { echo $comment['Comment']['title']. se realizará una petición al controlador para obtener los datos. Modificando la llamada a element para que se vea así: echo $this->element('latest_comments'. array('cache'=>'+1 hour')). De todos modos. Si estás usando parámetros con nombre en tu url. Esto permite a la llamada a requestAction evitar el uso de Router::url lo que puede incrementar el rendimiento. . los datos serán procesados y devueltos.} Si ahora creamos un elemento simple para llamar a esa función: // views/elements/latest_comments. entonces el array de url debe envolver los parámetros con nombre en la clave 'named'. array('return') ). A diferencia de otros lugares donde las urls de arrays son análogas a urls de cadenas. como array en requestAction debería ser: echo $this->requestAction( array( 'controller' => 'articles'. Esto incluye parámetros como $this->data y $this->params['form'] 3. mixed $id) La función loadModel es útil cuando se necesita usar un modelo que no es propiamente el modelo por defecto del controlador o uno de sus modelos asociados. $user = $this->User->read().4.Este. 2). 'action' => 'featured'. . 'named' => array( 'limit' => 3 ) ) ). array('limit' => 5. Cuando utilices una url de array junto con requestAction() has de especificar todos los parámetros que necesitarás en la acción pedida. $this->loadModel('Article'). 'order' => 'Article.7 loadModel loadModel(string $modelClass.created DESC')). $recentArticles = $this->Article->find('all'. requestAction las trata de manera diferente.4. $this->loadModel('User'.5. CakePHP incluye un conjunto fantástico de componentes listos para usar para conseguir ayuda con: • Seguridad • Sesiones • Lista de control de acceso (ACL) • Emails • Cookies • Autenticación • Manejo de pedidos (Requests) Cada uno de estos componentes del núcleo (Core) son detallados en su propio capitulo.6. El código anterior sería un ejemplo de configuración de las variables de componente del controlador beforeFilter() . function beforeFilter() { $this->Auth->authorize = 'controller'. debería antes considerar agrupar algunas funcionalidades en un componente. Algunos ejemplos de componentes que requieren ser configurados son: Auth. } $this->Cookie->name = 'CookieMonster'. Por el momento. 3.1 Configuración de componentes. Con esto ayudará a mantener el código de los controladores limpio y le será mas sencillo reusar código entre proyectos. Muchos de los componentes básicos requieren ser configurados. Cookie e Email. veremos como crear sus propios componentes.3. $this->Auth->loginAction = array('controller' => 'users'. Si tiene ganas de copiar y pegar código de un controlador a otro. Toda la configuración de estos componentes y los componentes en general se hacen en el método del controlador beforeFilter().6 Componentes # Introducción Los componentes son paquetes de lógica que son compartidos entre los controladores. 'action' => 'login'). 3. a través del cual podremos acceder a una instancia del mismo: /* Hace el nuevo componente accesible en $this->Math. $amount2) { return $amount1 + $amount2. Creariamos un componente que albergara esa lógica compartida para poder ser usada en diferentes controladores El primer paso es crear una nueva clase y fichero de componente.1 Añadiendo Componentes a tus Controladores Una vez finalizado nuestro componente.6. <?php class MathComponent extends Object { function doComplexOperation($amount1. 'randomGenerator' => 'srand' ). } } ?> 3. Este código pasaría el array conteniendo precision y randomGenerator al método intialize() de . así que no hay necesidad de redeclarar el mismo componente dos veces. el controlador recibirá un nuevo atributo con un nombre a partir del nombre del componente. 'Session'.6. Crea el fichero en /app/controllers/components/math. Los componentes declarados en AppController serán combinados con los de tus otros controladores.2. al igual que el standard $this->Session */ var $components = array('Math'. Cuando se incluyen Componentes a un Controlador tambien puedes declarar un conjunto de parámetros que serán pasados al método intialize() de los Componentes. 'Auth' ). La estructura básica para el componente quedaría así. Automáticamente.2 Creando Componentes a medida Supongamos que nuestra aplicación online necesita efectuar una compleja operación matemática en muchas partes de la aplicación. var $components = array( 'Math' => array( 'precision' => 2. podemos usarlo en los controladores de la aplicación añadiendo su nombre (excepto la parte "Component" al array $components del controlador.php. 'Session'). Estos parámetros pueden ser manejados por el Componente. Si deseas insertar algún código de lógica antes de que el controlador beforeFilter() sea llamado. <?php class MyComponent extends Object { // Este componente usa otros componentes var $components = array('Session'. Si por algún motivo no deseas que el método startup() sea llamado cuando el controlador está inicializando cosas. function doStuff() { $result = $this->Math->doComplexOperation(1. necesitarás usar el método initialize() del componente. necesitarás implementar el método initialize() o el startup(). $result). y el método startup() después del método beforeFilter.MathComponent como segundo parámetro. esta sintaxis no está implementada por ninguno de los Componentes Core. } } ?> También podrias querer utilizar otros componentes dentro del componente a medida. dale el valor true a la variable $disableStartup.2 Acceso a clases MVC dentro de componentes Para acceder a la instancia del controlador desde tu recien creado componente. Por ahora. simplemente crea una variable de clase $components (tal como lo harías en un controlador ) como un array que contenga los nombres de los componentes que quieres utilizar.2. El método initialize() es llamado antes del método beforeFilter(). Estos métodos especiales llevan una referencia al controlador como primer parámetro y son llamados automáticamente. <?php class CheckComponent extends Object { //llamado antse de Controller::beforeFilter() function initialize(&$controller) { // salvando la referencia al controlador para uso posterior $this->controller =& $controller. Para ello. 3. 'Math'). $this->Session->write('stuff'. } .6. } //llamado tras Controller::beforeFilter() function startup(&$controller) { } function redirectSomewhere($value) { // ulizando un método de controlador $this->controller->redirect($value). 2). 6. } } ?> 3. Aquí tienes un ejemplo: <?php class MathComponent extends Object { function doComplexOperation($amount1. excepto por su dependencia el uno del otro. function initialize(&$controller) { $this->Hijo->foo()..} ?> No es muy recomendable acceder o usar un modelo en un componente. } } . $totalUsers = $userInstance->find('count'). y cada componente hijo tiene acceso a su padre.. Declaración del padre: <?php class PadreComponent extends Object { var $name = "Padre". En cambio puedes hacer que tu componente sea un "Padre" e indicarle con el array $components la lista de sus "Hijos". $amount2) { $userInstance = ClassRegistry::init('User'). var $components = array( "Hijo" ). Los componentes padres se cargan antes que sus componentes hijos. $amount2) { return $amount1 + $amount2. tendrás que instanciar tu clase modelo y usarla manualmente. pero si tras sopesar las posibilidades eso es lo que quieres hacer. entonces no querrás ponerlos dentro de un solo componente. } function doUberComplexOperation ($amount1. } function bar() { // .3 Usando Componentes en tu Componente A veces uno de tus componentes puede depender de otro. return ($amount1 + $amount2) / $totalUsers.2. Si las funcionalidades que estos componentes proveen no están relacionados. } } . } function foo() { // .. function initialize(&$controller) { $this->Padre->bar().Declaración del hijo: <?php class HijoComponent extends Object { var $name = "Hijo".. Crea tu fichero modelo en PHP en el directorio /app/models/ o en un subdirectorio de /app/models/. o una casa.3. Es esta clase Model interna la que otorga la funcionalidad a nuestro modelo pesonalizado. } ?> Simplemente con esta declaración. Estos métodos mágicos provienen del modelo de herencia de CakePHP.php que reside en la raíz de la carpeta /app/.php) CakePHP utilizará AppModel en lugar de tu archivo de modelo con nombre incorrecto. ingredient. es un objeto que representa una "cosa". Ingredient. Esto también significa que si. La clase intermedia AppModel está vacía y reside por defecto dentro de la carpeta /cake/. Redefinir AppModel te permitirá definir funcionalidad que debería estar disponible a todos los modelos dentro de tu aplicación. puede contener varios artículos (posts) y cada artículo puede contener varios comentarios. Bake generará automáticamente este fichero por ti. por ejemplo. cada uno asociado con el otro.php o ingredients.7.1 La Comprensión de Modelos Un Modelo representa tu modelo de datos y. una persona. Si estás tratando de utilizar un método de un modelo personalizado y estás obteniendo errores SQL. accidentalmente. en programación orientada a objetos.php. interna de CakePHP. necesitas crear tu propio fichero app_model. Aquí presentamos un ejemplo simple de definición de modelo en CakePHP: <?php class Ingredient extends AppModel { var $name = 'Ingredient'. Creando un proyecto utilizando Bake. Ingredient. normalmente es porque CakePHP . para este ejemplo.7 Modelos 3. Para hacer eso. Los objetos Blog. nombras tu archivo de manera errónea (p. Un blog. Por convención. Artículo (Post) y Comentario son ejemplos de modelos. el cual extiende la clase Model. El modelo Ingredient extiende el modelo de aplicaciónm AppModel. CakePHP lo encontrará en cualquier lugar en el directorio.ej. debería tener el mismo nombre que la clase. CakePHP creará dinamicamente un objeto modelo por ti si no puede encontrar un archivo correspondiente en /app/models. como un coche. se le otorga al modelo Ingredient toda la funcionalidad que necesitarás para crear consultas junto con guardado y borrado de datos. 'User'). } } ?> Si no has añadido el modelo a través de la propiedad $uses entonces necesitarás importar el modelo manualmente e instanciarlo dentro de la acción. $ingredients). <?php class RecipeController extends AppController { var $uses = array('Recipe'. En el siguiente ejemplo. $this->set('ingredients'. En el siguiente ejemplo. Como podrás ver en Controllers. un controlador llamado IngredientsController automaticamente inicializará el modelo Ingredient y será accesible por el controlador mediante $this->Ingredient. Ver también Comportamientos para más información sobre cómo aplicar lógica similar para múltiples modelos. ambos modelos Recipe y User son accesibles desde el controlador actual. . $this->User->find('all'). Por ejemplo. CakePHP automaticamente hará que se pueda acceder al modelo cuando su nombre concuerde con el del controloador. $this->Recipe->Ingredient->find('all'). este puede ser accedido dentro de tu Controlador. La propiedad $name es necesaria para PHP4 pero opcional para PHP5. Con tu modelo definido. } } ?> Están disponibles los modelos asociados a través del modelo principal. function index() { $this->Recipe->find('all'). puedes atar múltiples modelos al controlador y acceder directamente desde él. el modelo Receta (Recipe) tiene una asociación con el modelo Ingrediente (Ingredient). <?php class IngredientsController extends AppController { function index() { //obtiene todos los ingredientes y los pasa a la vista: $ingredients = $this->Ingredient->find('all').no puede encontrar tu modelo. CakePHP inspeccionará tus tablas para determinar el tipo de dato de cada campo y utiliza esta información apra automatizar varias características como la salida de campos de formulario en la vista. CakePHP está diseñado para ser agnóstico y funcionará con MySQL. un nombre de Modelo de Ingredient espera el nombre de tabla ingredients. automáticamente se mapean a las tablas que has creado. en minúsculas y en plural. PostgreSQL y otros. En el resto de esta sección verás cómo CakePHP "mapea" tipos de campos de bases de datos en tipos de datos PHP y cómo CakePHP puede automatizar tareas basandose en cómo tus campos están definidos. MSSQL. por convención. Puedes crear tus tablas de base de datos como lo harías normalmente. Los nombres de las tablas son. Las asociaciones del modelo con el nombre de la tabla pueden ser anuladas con el atributo useTable del modelo. Oracle. Por ejemplo.7. App::import('Model'. Cuando creas tus clases del Modelo. CakePHP viene preparado para el inglés. } ?> } 3. la mayoría de las veces lo son. con las palabras de los nombres de tablas de varias palabras separadas por guiones de subrayado (_). en minúscula y separados por guiones de subrayado (_). un nombre de Modelo de EventRegistration debería esperar un nombre de tabla event_registrations.php . En caso de desear flexiones para el español es necesario modificar eL fichero cake/libs/inflector. por convención. Los nombres de los campos son. function index() { $this->Recipe->find('all').2 Creando Tablas de Bases de Datos A pesar de que CakePHP puede tener orígenes de datos (datasources) que no son manejadas por sistemas de gestión de bases de datos. $user->find('all'). explicado más adelante en este capítulo. 'User'). $user = new User().<?php class RecipeController extends AppController { var $uses = array('Recipe'). 1 MySQL Tipo CakePHP Propiedades del Campo primary_key NOT NULL auto_increment string varchar(255) text text integer int(11) float float datetime datetime timestamp datetime time time date date binary blob boolean tinyint(1) Un campo de tipo tinyint(1) es considerado booleano por CakePHP. El siguiente desglose describe cómo está "mapeado" cada uno.7.3. CakePHP "mapea" dichos tipos a algo que reconoce y crea una interfaz unificada sin importar en qué sistema de bases de datos necesitas ejecutarlo. Dentro de la clase de origen de datos (o "fuente de datos".2.2. 3.7.1.7.1 Asociaciones de Tipo de Dato por Base de Datos Todo RDMS define tipos de datos de manera ligeramente diferente.1. 3. datasource) para cada sistema de base de datos.2.2 MySQLi Tipo CakePHP primary_key string text integer float datetime timestamp time date binary boolean Propiedades del Campo DEFAULT NULL auto_increment varchar(255) text int(11) float datetime datetime time date blob tinyint(1) . i.2.2.7.i.3 ADOdb Tipo CakePHP primary_key string text integer float datetime timestamp time date binary boolean 3.s) date (Y-m-d) blob smallint(1) Tipo CakePHP primary_key string text integer float Propiedades del Campo IDENTITY (1.1. 1) NOT NULL varchar(255) BLOB SUB_TYPE 1 SEGMENT SIZE 100 CHARACTER SET NONE integer float .1.5 Firebird/Interbase Propiedades del Campo not null generated by default as identity (start with 1.i.2.3.7.s) time (H.1.s) timestamp (Y-m-d-H. increment by 1) varchar(255) clob integer(10) double timestamp (Y-m-d-H.7.4 DB2 Propiedades del Campo R(11) C(255) X I(11) N T (Y-m-d H:i:s) T (Y-m-d H:i:s) T (H:i:s) T (Y-m-d) B L(1) Tipo CakePHP primary_key string text integer float datetime timestamp time date binary boolean 3. 7. 1) NOT NULL varchar(255) text int numeric datetime (Y-m-d H:i:s) timestamp (Y-m-d H:i:s) datetime (H:i:s) datetime (Y-m-d) image bit 3.Y H:i:s) timestamp (d.1.Y H:i:s) time (H:i:s) date (d.m.2.7.m.Y) blob smallint Tipo CakePHP primary_key string text integer float datetime timestamp time date binary boolean Propiedades del Campo IDENTITY (1.2.datetime timestamp time date binary boolean 3.1.7 Oracle Tipo CakePHP primary_key string text integer float datetime timestamp time date binary boolean number inet Propiedades del Campo number NOT NULL varchar2(255) varchar2 numeric float date (Y-m-d H:i:s) date (Y-m-d H:i:s) date (H:i:s) date (Y-m-d) bytea boolean numeric inet .m.6 MS SQL timestamp (d. 2.2.10 Sybase Propiedades del Campo integer primary key varchar(255) text integer float datetime (Y-m-d H:i:s) timestamp (Y-m-d H:i:s) time (H:i:s) date (Y-m-d) blob boolean Tipo CakePHP primary_key string text integer float datetime timestamp Propiedades del Campo numeric(9.3.9 SQLite Propiedades del Campo serial NOT NULL varchar(255) text integer float timestamp (Y-m-d H:i:s) timestamp (Y-m-d H:i:s) time (H:i:s) date (Y-m-d) bytea boolean numeric inet Tipo CakePHP primary_key string text integer float datetime timestamp time date binary boolean 3.1.2.7.1.7.0) IDENTITY PRIMARY KEY varchar(255) text int(11) float datetime (Y-m-d H:i:s) timestamp (Y-m-d H:i:s) .7.1.8 PostgreSQL Tipo CakePHP primary_key string text integer float datetime timestamp time date binary boolean number inet 3. 3. . CakePHP reconocerá esos campos y los rellenará automaticamente cuando un registro sea creado o grabado en la base de datos.2 Titulos datetime (H:i:s) datetime (Y-m-d) image bit Un objeto. CakePHP generará automáticamente UUIDs (Identificadores Únicos Universales) cuando son creados nuevos registros.7.2.2. Alternativamente. Una entrada de un blog tiene un título. El campo modified será actualizado con la fecha y hora actuales cuando el registro existente sea grabado. Estos campos necesitan ser del tipo datetime con el valor por defecto establecido a NULL para ser reconocidos por CakePHP.4 Utilizando UUIDs como Claves Primarias Las claves primarias son normalmente definidas como campos INT. en sentido físico. a menudo tiene un nombre o un título con el que referirse.7. para cada nuevo registro que se añade. Una persona tiene un nombre como Juan o Ambrosio o Colega. vistas de árbol Si tienes un campo title y un campo name en tu tabla. La base de datos incrementará automáticamente el campo. Nota: Un campo llamado updated (actualizado) exhibirá el mismo comportamiento que modified.normalmente utilizado para los desplegables <select> • TreeBehavior — reordenación.time date binary boolean 3. Los campos created y modified serán establecidos a la fecha y hora actuales cuando el registro es inicialmente añadido.7. etiquetas de fieldset • Listas . comenzando en 1. CakePHP automáticamente utilizará esta etiqueta en varias circunstancias: • Scaffolding — títulos de páginas. Una categoría tiene un nombre. Al especificar el campo title (título) o name (nombre). el campo title será el utilizado.2. si especificas tu clave primaria como CHAR(36). 3.3 Creado y modificado ("created" y "modified") Al definir un campo created (creado) o modified (modificado) en tu tabla de la base de datos como campo de tipo datetime. 'Post.Un UUID es una cadena de 32 bytes separada por guiones.created'. array( . array( 'fields'=>array('Post. //int 'page' => n //int campos ) Si estás utilizando find('list').title') ) ).id. array('fields'=>'Post. $parametros es un array con cualquiera de las siguientes opciones disponibles como claves: array( 'conditions' => array('Model. Por ejemplo: 550e8400-e29b-41d4-a716-446655440000 Los UUIDs están diseñados para ser únicos.id. valor y grupo // la lista generada será indexada por Post.author_id. y cada grupo indexado por Post. la clave 'fields' en $parametros define la clave.1 find find($tipo.title $this->Post->find('list'. // la lista generada será indexada por Post. con valor de Post.7.3.slug.title')). // la lista generada será agrupoada por Post. 3. //campos para GROUP BY 'limit' => n. //array de nombres de 'order' => 'Model. 'count'. con valor de Post.3 Recuperando tus Datos 3. //int 'fields' => array('Model. con un total de 36 caracteres.field' => $thisValue). Si necesitas que un campo permanezca único a través de sistemas. 'Model.title $this->Post->find('list'. //array de condiciones 'recursive' => 1. 'neighbors'. 'first' es el tipo de búsqueda predeterminado.field'). 'first'. //string o array definiendo el orden 'group' => array('Model. sino también a través de tablas y bases de datos.7. 'list' o 'threaded'. $parametros) $tipo es 'all'. los UUIDs son un genial enfoque.title $this->Post->find('list'. no sólo dentro de una tabla dada.slug'. con valor de Post.field1'.field2'). 7. array('order' => array('Article. array $campos. int $recursivo) 3. find también acepta la sintasix previa: find(string $condiciones. $this->Article->order = null.id' => 1))). Notar que las claves 'field' y 'value' no son usadas para find('all') y este es un caso especial para find('neighbors'). $two = $this->Post->find('neighbors'. array('conditions' => array('Article.title'.. 'value'=> $data['Post']['title']) ). Para compatibilidad hacia atraś. y la clave 'value' en el array $parametros define el valor a mirar para determinar el siguiente y el anterior. 'fields'=> array('Post. veremos assuming we have id's from 110.por lo tanto ningún criterio de condición o de ordenamiento será utilizado. // para obtener los datos vecinos utilizando un campo diferente. } En este primer ejemplo. Abajo hay un par de ejemplos simples (código del controlador [controller]): function some_function() { .3. veremos <em>prev</em> establecido a 1 y <em>next</em> establecido a 3 $this->Post->id = 2. // reseteando si ya ha sido inicializado $semiRandomArticle = $this->Article->find().author_id') ) Si estás utilizando find('neighbors').title'. $one = $this->Post->find('neighbors'). 'Post.1.. ningún parámetro se le ha pasado a find . // simulando el modelo teniendo un órden por defecto $lastCreated = $this->Article->find().. // asumiendo que tenermos id's de 1 a 10.). 'Post.. $params) 'first' es el tipo find por defecto.created DESC'.id'.. $specificallyThisOne = $this->Article->find('first'. string $orden. la clave 'field' en $parametros define el campo a analizar. $alsoLastCreated = $this->Article->find('first'. El formato devuelto por la llamada a find('first') .created DESC'))). array( 'field'=> 'Post.1 find('first') find('first'. y devolverá un solo resultado. . $this->Article->order = 'Article. deberías utilizar esto para cualquier caso donde esperes solo un resultado.. dictatado por las conditions (condiciones)). 3.será de la siguiente forma: Array ( [ModelName] => Array ( [id] => 83 [field1] => value1 [field2] => value2 [field3] => value3 ) [AssociatedModelName] => Array ( [id] => 1 [field1] => value1 [field2] => value2 [field3] => value3 ) ) No hay parámetros adicionales usador por find('first')..3 find('all') find('all'. Podrías necesitar campos específicos para DISTINCT count (de lo contrario.status !=' => 'pending') )). $pending = $this->Article->find('count'. array('conditions' array('Article. No hay parámetros adicionales usados con find('count').7. .7. $params) .. count es siempre lo mismo .user_id'.1. array( 'fields' => 'DISTINCT Article. $params) Devuelve un valor entero. $publishedAuthors = $this->Article->find('count'. $total = $this->Article->find('count').3. 'conditions' => array('Article.3. $authors = $this->Article->User->find('count'). 3. } => No pasar campos como arrays a find('count').1.2 find('count') find('count'... Debajo hay un par de ejemplos sencillos (código controlador): function some_function() { .status' => 'pending'))). 1..Devuelve un array de resultados(potentially multiple). $allArticles = $this->Article->find('all'). es. array('conditions' => array('Article. el mecanismo usado por todas las variantes del método find(). $allPublishedAuthors = $this->Article->User->find('all'.3. Debajo puedes ver un par de (código controlador) ejemplos: function some_function() { . $params) Devuelve un array indexado.4 find('list') find('list'.status' => 'pending'))). útil para cualquier uso donde podrías querer una lista como los polulares campos select de los formularios. como por ejemplo para paginar. Debajo hay un par de simples ejemplos (código . de hecho. no se le han aplicado condiciones a find. } El ejemplo de abajo $allAuthors busca todos los campos de la tabla users. .. $pending = $this->Article->find('all'. array('conditions' => array('Article. $allAuthors = $this->Article->User->find('all').7.status !=' => 'pending'))). 3... Los resultados de llamar a find('all') serán de la siguiente forma: Array ( [0] => Array ( [ModelName] => Array ( [id] => 83 [field1] => value1 [field2] => value2 [field3] => value3 ) [AssociatedModelName] => Array ( [id] => 1 [field1] => value1 [field2] => value2 [field3] => value3 ) ) ) Aquí no hay parámetros condicionales usados por find('all'). $allPublishedAuthors = $this->Article->User->find('list'. no se le aplica ninguna condición para filtrar la búsqueda que lleva a cabo find.. array('User. array('User. 'displayValue5'.group')).. 'displayValue3'. 'displayValue2'.first_name'. } array('fields' array('fields' array('fields' => => => .. Por defecto la clave primaria para el modelo es usada por la key.status' => 'pending'))). . array('conditions' => array('Article. 'User. Algunos ejemplos aclarará un poco más: function some_function() { . $allArticles = $this->Article->find('list'). } En el ejemplo siguiente $allAuthors va a contener todos los usuarios de la tabalo usuers. $pending = $this->Article->find('list'.. 'displayValue4'. 'displayValue1'..first_name')). 'User.status !=' => 'pending'))).controlador): function some_function() { .. los parámetros pasados son usados para determinar que debería ser usado como la key del array. Cuando find('list') es llamado.username'. array('conditions' => array('Article. $justusernames = $this->Article->User->find('list'. array('User. 'displayValue6'. $usernameMap = $this->Article->User->find('list'.. y el valor que se muestra es el usado por el value. .username'..username')). $allAuthors = $this->Article->User->find('list'). value y opcionalmente a que grupo pertenecen los resultados. $usernameGroups = $this->Article->User->find('list'. Los resultado tras llamar al método find('list') tendrán el siguiente aspecto: Array ( //[id] [1] => [2] => [4] => [5] => [6] => [3] => ) => 'displayValue'. 'User. ['PHPNut'] => 'Larry'. ) $usernameMap = Array ( //[username] => 'firstname'. // not the root $someCategories = $this->Category->find('threaded'. ) $usernameGroups = Array ( ['Uber'] => Array ( ['PHPNut'] => 'Larry'. ['AD7six'] => 'Andy'. ) ['Admin'] => Array ( ['_psychic_'] => 'John'.7. Abajo se muestran un par de ejemplos (código controlador): function some_function() { . array('conditions' array('parent_id' => 42)). [2] => 'gwoo'. y es apropiado si quieres usar el campo parent_id de tu modelo de datos para construir resultados anidados.3. [400] => 'jperras'. [213] => 'AD7six'. ['AD7six'] => 'Andy'.5 find('threaded') find('threaded'. ['_psychic_'] => 'John'. ['jperras'] => 'Joël'. $aCategory = $this->Category->find('first'. el resultado devuelto se parecería a esto: $justusernames = Array ( //[id] => 'username'. $params) Devuelve un array anidado.1. [25] => '_psychic_'. array( 'conditions' => array( => . ['gwoo'] => 'Gwoo'. $allCategories = $this->Category->find('threaded'). ) ) 3.En el anterior ejemplo.. ['gwoo'] => 'Gwoo'. ['jperras'] => 'Joël'. [1] => 'PHPNut'.. . The results of a call to find('threaded') will be of the following form: Array ( [0] => Array ( [ModelName] => Array ( [id] => 83 [parent_id] => null [field1] => value1 [field2] => value2 [field3] => value3 ) [AssociatedModelName] => Array ( [id] => 1 [field1] => value1 [field2] => value2 [field3] => value3 ) [children] => Array ( [0] => Array ( [ModelName] => Array ( [id] => 42 [parent_id] => 83 [field1] => value1 [field2] => value2 [field3] => value3 ) [AssociatedModelName] => Array ( [id] => 2 [field1] => value1 [field2] => value2 [field3] => value3 .lft >=' => $aCategory['Category']['lft']. } 'Article. pero todos los resultados deseados deben poderse encontrar en una sencilla consulta. result for $aCategory and everything below it.) )). . nested. $allCategories contendría un array anidado representando la estuctura entera de la categoría. 'Article. El segundo ejemplo hace uso de la estructura de datos Tree behavior the return a partial. El anterior ejemplo.rght <=' => $aCategory['Category']['rght'] No es necesario utilizar el comportamiento en árbol para usar este método.. } En este ejemplo podemos ver dos elementos esenciales del arreglo $params: 'field' y 'value'. ) [children] => Array ( ) El orden en el que aparecen los resultados puede ser cambiado como lo es la influencia de la orden de procesamiento..1. deberías de utilizar 'contain' en el arreglo $params). array('field' => 'id'. El formato de salida para una llamada find('neighbors') es de la siguiente forma: Array ( [prev] => Array ( [ModelName] => Array ( [id] => 2 [field1] => value1 [field2] => value2 . Por ejemplo.) ) ) ) . ) [AssociatedModelName] => Array .7. se pueden utilizar otros elementos que se utilizan en las demás implementaciones del método find (Por ejemplo: Si tu modelo actúa como contenedor.. $params) 'neighbors' realiza una búsqueda similar a 'first'. Además de estos.. a diferencia que devuelve el registro precedente y posterior del solicitado. there is no inbuilt requirement of this method for the top result to be returned first. 'value' => 3)). los resultados van a aparecer en orden según el nombre. No hay parámetros adicionales usados por find('threaded').6 find('neighbors') find('neighbors'. 3. si 'order' => 'name ASC' es pasado en los parámetros a find('threaded'). A continuación un (código en controlador) ejemplo: function some_function() { $neighbors = $this->Article->find('neighbors'.. Del mismo modo cualquier orden puede ser usado.3. order_status = 3 Recipe. Ejemplo findAllBy<x> en PHP5 $this->Product->findAllByOrderStatus(‘3’)...( [id] => 151 [field1] => value1 [field2] => value2 ..3.last_name = ‘Anderson’ Cake..type = ‘Cookie’ User. ) [AssociatedModelName] => Array ( [id] => 122 [field1] => value1 [field2] => value2 . $this->Recipe->findAllByType(‘Cookie’). ) ) ) Note que el resultado siempre tendrá dos arreglos principales: prev y next.7. 3.3 findBy findBy<nombreCampo>(string $valor) Estas funciones mágicas pueden ser usadas como atajo en la búsqueda en tus tablas por cierto campo.3. Simplemente añade el nombre del campo (en formato CamelCase) al final del nombre de esas funciones (<nombreCampo>) y proporciona los criterios para ese campo como primer parámetro. $this->User->findAllByLastName(‘Anderson’).2 findAllBy findAllBy<nombreCampo>(string $valor) Estas funciones mágias pueden ser usadas como atajos para buscar en tus tablas por cierto campo. 3.7. y proporciona los criterios para ese campo como primer parámetro. $this->Cake->findById(7). Fragmento SQL Correspondiente Product. ) ) [next] => Array ( [ModelName] => Array ( [id] => 4 [field1] => value1 [field2] => value2 ... Simplemente añade el nombre del campo (en forma CamelCase) al final de las funciones (<nombreCampo>).id = 7 . 3.$this->User->findByUserName(‘psychic’). en vez del nombre del modelo. query() utiliza el nombre de la tabla en la consulta como clave del array de datos devueltos. Product. User.type = ‘Cookie’ $this->User->findAllByLast_name(‘Anderson’). Por ejemplo: $this->Fotografia->query("SELECT * FROM fotografias LIMIT 2.order_status = 3 $this->Recipe->findAllByType(‘Cookie’).3. Recipe. debería devolver Array ( [0] => Array ( [fotografías] => Array ( [id] => 1304 [user_id] => 759 ) ) [1] => Array ( [fotografías] => Array ( [id] => 1305 [user_id] => 759 ) ) .").7. la cual ayuda a limpiar datos de usuario de injection y ataques de cross-site scripting.user_name = ‘psychic’ Los usuarios de PHP4 han de utilizar esta función de manera un poco diferente debido a cierto caseinsensitivity en PHP4: Ejemplo findAllBy<x> en PHP4 Fragmento SQL Correspondiente $this->Product->findAllByOrder_status(‘3’). no olvides leer la sección Desinfección de Datos (Sanitization) de CakePHP.4 query query(string $consulta) Se pueden realizar llamadas SQL personalizadas usando el método query() del modelo. Si alguna vez usas consultas SQL personalizadas en tu aplicación.user_name = ‘psychic’ El resultado devuelto es un array formateado tal y como sería en find() o findAll(). Cake. User.id = 7 $this->User->findByUser_name(‘psychic’).last_name = ‘Anderson’ $this->Cake->findById(7). User. la consulta puede ser reescrita: $this->Fotografia->query("SELECT * FROM fotografia AS Fotografia LIMIT 2.7. $fields es usado para especificar un nombre de campo.3. como cadena."). especificado en $name. la cual devuelve Array ( [0] => Array ( [Fotografia] => Array ( [id] => 1304 [user_id] => 759 ) ) [1] => Array ( [Fotografia] => Array ( [id] => 1305 [user_id] => 759 ) ) ) 3. $id) read() es un método usado para establecer los datos del modelo actual (Model::$data)-así también mientras se está editando--pero también puede ser usado en otras circunstancias para obtener un solo registro de la base de datos.) Para usar el nombre del modelo como clave del array. y obtener un resultado consistente con el devuelto por los métodos Find. del primer registro que cumpla $condiciones estando ordenado por $orden. string $condiciones.5 field field(string $nombre. o un arreglo de nombres de campo que serán incluidos en la consulta. 3. entonces todos los campos serán incluidos. string $orden) Devuelve el valor de un campo singular.7.6 read() read($fields.3. . si no se especifica un valor. es usado. $id2). $record = $this->data // almacena el tercer registro en <code>$record</code> . // $this->Article->read().7 Condiciones Complejas de Búsqueda La mayoría de las llamadas de búsqueda del modelo involucran pasar conjuntos de condiciones de una u otra manera. // obtiene el <em>rating</em> del segundo registro $this->id = $id3. 3. Las lineas 6-8 demuestran como read() cambia los datos del modelo actual. Esta sintaxis también particiona los elementos de tu consulta (campos. // lee un tercer registro. operadores.3. // Ejemplo de uso con un modelo: $this->Articulo->find($condiciones). En su forma más básica. $rating = $this->read('rating'). $rating = $this->read('rating'). una consulta basada en array es así: $condiciones = array("Articulo. Si ves que necesitas más control. $id2). especificado por <code>$id3</code>.$id especifica el ID de registro que será leído. Usar arrays permite una lectura más clara y fácil.. Notar que podríamos haber utilizado como nombre de campo . // obtiene el nombre un segundo registro.. La aproximación más simple a ello es utilizar la cláusula WHERE de SQL. puedes utilizar arrays.7. Esto es porque read() cambia el valor en Model::$id a cualquier valor pasado como $id.title" => "Esto es un artículo"). especificado por Model::$id..) en partes discretas y manipulables. function beforeDelete($cascade) { . Esto permite a CakePHP generar la consulta más eficiente posible.. el registro actualmente seleccionado. } Notar que la tercera llamada a read() obtiene el rating del mismo registro leído anteriormente por la llamada $this->read('name'. valores. etc. La estructura aquí es bastante autoexplicativa: buscará cualquier artículo donde el título sea igual a "Esto es un artículo". Si se especifica un valor diferente a $id causará que el registro que cumpla con la condición será seleccionado. asegurar una sintaxis SQL apropiada. y formatear apropiadamente cada parte individual de la consulta. y también hace muy fácil la construcción de consultas. $name = $this->read('name'. Por defecto. // obtiene el <em>rating</em> del registro que será borrado. CakePHP puede analizar sintácticamente cualquier operador de comparación en SQL. devolverá artículos que nunca han sido modificados).) para encontrar artículos cuyo título no está en el conjunto de valores dado: array( "NOT" => array( "Articulo. "Tercer artículo") ) Para realizar una búsqueda con condición NOT IN(. Digamos que querías buscar artículos donde el título estaba dentro de un conjunto dado de valores: array( "Articulo. pero cuando se construyen consultas es buena práctica especificar siempre el nombre del modelo (en este caso. Digamos que queremos buscar todos los artículos donde el título no sea 'Esto no es un artículo': array("Articulo.. strtotime("-2 weeks")) ) También puedes crear búsquedas que comparen dos campos en la base de datos: array("Articulo. La unica excepción aquí es la condición de búsqueda del tipo IN (.. .modified") Este ejemplo de arriba devolverá artículos en los cuales la fecha de creación es igual a la fecha de modificación (p.title" => array("Primer artículo". ¿Qué hay sobre otros tipos de condiciones? Estas son igualmente simples. en cuyo caso deberías modificar tu esquema de tablas.title" => array("Primer artículo".created >" => date('Y-m-d'. "Segundo artículo". BETWEEN. "Articulo.).created = Articulo. "Tercer artículo"). siempre y cuando dejes un espacio entre el nombre del campo y el operador.e.title" => array("Primer artículo". ya que mejora la claridad del código y ayuda a prevenir colisiones en el futuro. Articulo).title <>" => "Esto no es un artículo") Notar el '<>' que está detrás del nombre del campo.. incluyendo las expresiones usando LIKE.. o REGEX. "Segundo artículo". "Segundo artículo".simplemente 'title'. "Tercer artículo") ) ) Añadir filtros adicionales a las condiciones es tan simple como añadir pares clave/valor adicionales al array: array ( "Articulo. ...name" => "Pedro". "Segundo artículo".id BETWEEN ? AND ?' => array(1.title" => null. la consulta devolverá registros en los que el título del artículo no es nulo: array ( "not" => array ( "Articulo. "Articulo.. Estas condiciones son también infinitamente anidables. podemos igualmente buscar artículos que coincidan con cualquiera de las condiciones: array( "or" => array ( "Articulo. puedes usar lo siguiente: array('Articulo. y pueden estar en mayúsculas o minúsculas. No obstante.created >" => date('Y-m-d'. es decir. Digamos que tienes una relación belongsTo entre Articulos y Autores. "magico") o que han sido creados en las últimas dos semanas. ) ) Para manejar consultas con BETWEEN. Digamos que quieres buscar todos los artículos que contienen una cierta palabra (p. las condiciones de más arriba sólo coincidirán con artículos que han sido creados en las últimas dos semanas (-2 weeks). "Tercer artículo"). strtotime("-2 weeks")) ) ) Cake también puede comprobar campos nulos (null).10)) Nota: CakePHP entrecomillará los valores numéricos dependiendo del tipo de campo definido en tu base de datos. como prefieras. OR.e. "or" => array ( "Articulo.created >" => date('Y-m-d'..title" => array("Primer artículo". En este ejemplo. strtotime("-2 weeks")) ) ) Cake acepta todas las operaciones booleanas de SQL válidas.title LIKE" => "%magico%".).. .Por defecto. NOT. y posean un título que coincida con alguno de los dados en el conjunto ("Primer artículo". CakePHP junta múltiples condiciones con AND booleano. pero quieres restringir tu búsqueda a artículos escritos por Pedro: array ( "Autor. etc. "Articulo. XOR. incluyendo AND. Las cuales producen el siguiente código SQL: SELECT `Compania`.`description`. 'suspendido')) ) ) ) ) ).4 Guardando Tus Datos CakePHP hace que el salvado de los datos del modelo sea instantáneo.`size` FROM `companias` AS `Compania` WHERE ((`Compania`.`name` = 'Emporio Futuro') OR (`Compania`.`name`.`status`. array('Compania.name' => 'Emporio Futuro'). 'suspendido')))) 3.status'=> array('inactivo'. `Compania`.Puedes crear condiciones muy complejas anidando múltiples arrays de condiciones: array( 'OR' => array( array('Compania. Los datos listos para ser salvados deberán ser pasados al método save() del modelo usando el formato básico siguiente: Array ( [NombreModelo] => Array ( [nombrecampo1] => 'valor' [nombrecampo2] => 'valor' ) ) . `Compania`.status' => 'activo').`location`. `Compania`.`created`. `Compania`.`name` = 'Megatrabajos de Acero')) AND ((`Compania`.7.`status` = 'activo') OR (NOT (`Compania`.`status` IN ('inactivo'. `Compania`. `Compania`.name' => 'Megatrabajos de Acero') ).`id`. 'AND' => array( array( 'OR'=>array( array('Compania. 'NOT'=>array( array('Compania. .. el identificador ID del objeto se encuentra en el atributo $id del objeto del modelo (algo especialmente útil cuando se crean nuevos objetos). busca la receta a editar y pásala a la vista $this->set('receta'. array $listaCampos = array()) Mostrado arriba. puedes limitar los campos grabados a aquellos listados en $listaCampos. boolean $validar = true. los datos pasados a la función como primer parámetro son validados usando el mecanismo de validación de CakePHP (ver el capítulo de validación de datos para más información). FormHelper. los datos también están convenientemente disponibles en $this>data para su uso rápido. $nuevoIngredienteId = $this->Ingrediente->id. } } // Si no hay datos de formularo. $this->Receta->findById($id)).La mayoría de las veces no necesitarás preocuparte por este formato: los ayudantes de CakePHP HtmlHelper.. y el tercero ($listaCampos) te permite proveer una lista de campos del modelo a ser grabados. $this->Session->setFlash("Receta guardada!"). Aquí está un ejemplo rápido de una acción de un controlador que usa un modelo de CakePHP para salvar datos en una tabla de una base de datos: function edit($id) { // Ha POSTeado algún dormulario datos? if(!empty($this->data)) { // Si el formulario puede ser validado y salvado. y métodos de búsqueda empaquetan los datos en este formato. El segundo parámetro ($validar) te permite eludir la validación. Si por alguna razón tus datos no se graban. if($this->Receta->save($this->data)) { // Establede un mensaje flash y redirige. este método graba datos formateados en array. Hay unos pocos métodos relacionados con el salvado que encontrarás útiles: save(array $datos = null. Si estás usando alguno de los ayudantes. $this->redirect('/recetas'). $this->Ingrediente->save($datosNuevos). Una vez que un salvado ha sido completado. } Una nota adicional: cuando se llama a save(). comprueba si alguna regla de validación se está incumpliendo. Como seguridad añadida. y los campos a ser actualizados. saveField(string $nombreCampo. 'Un Nuevo Título para un Nuevo Día').vendedor_id' => 453) ). Los registros a ser actualizados están identificados por el array $conditions. Establece el ID del modelo ($this>nombreModelo->id = $id) antes de llamar a saveField(). Cuando usas este método. Los valores literales deberían ser entrecomillados manualmente. . Por ejemplo. array('Ticket. no el nombre del modelo y campo. strtotime('-1 year')). $nombreCampo debería contener sólo el nombre del campo. array $condiciones) Actualiza varios registros en una única llamada. están identificados por el array $fields. la llamada a saveField desde un controlador debería parecerse a esto: $this->Entrada->saveField('titulo'. $this->Panadero->updateAll( array('Panadero. Por ejemplo. Por ejemplo. para aprobar a todos los panaderos que han sido miembros durante más de un año. la instancia del modelo estará lista para salvar con esos datos (accesibles en $this->data). updateAll(array $campos.approved' => true). para actualizar el título de una entrada de un blog.estado' => "'cerrado'").Cuando se llama a save() en un bucle. no olvides llamar a create(). $validar = false) Usado para salvar un único valor de un campo. array('Panadero. así como sus valores. la llamada de actualización debería ser algo como: $este_año = date('Y-m-d h:i:s'. create(array $datos = array()) Este método resetea el estado del modelo para grabar nueva información. El array $campos acepta expresiones SQL.created <=' => "$este_año") ). Si se pasa el parámetro $datos (usando el formato de array descrito arriba). para cerrar todos los tickets que pertenecen a cierto vendedor: $this->Ticket->updateAll( array('Ticket. string $valorCampo. $data necesita ser un array de registros indexado numéricamente como esto: Array ( [0] => Array ( ) [1] => Array ( [titulo] => titulo 1 [titulo] => titulo 2 ) ) Para salvar un registro junto con su registro relacionado teniendo una asociación hasOne o belognsTo. el array de datos debería ser como: Array ( [Usuario] => Array ( [nombreusuario] => billy ) [Perfil] => Array ( [sexo] => Varon [ocupacion] => Programador ) ) Para salvar un registro junto con sus registros relacionados teniendo una asociación hasMany. el array de datos debería ser como: Array ( [Articulo] => Array ( [titulo] => Mi primer artículo ) [Comentario] => Array ( [0] => Array ( [comentario] => Comment 1 [comentario] => 1 ) [1] => Array . array $opciones = array()) Usado para salvar (a) múltiples registros individuales para un único modelo o (b) este registro así como todos los registros asociados. Para salvar múltiples registros de un único modelo.saveAll(array $datos = null. Si ninguno de los registros de los modelos asociados existe aún (por ejemplo. En la acción de ejemplo mostrada abajo se asumirá que has POSTeado sufientes datos (usando el FormHelper) para crear un solo Usuario y un solo Perfil. durante la operación de guardado. hasMany y belongsTo ('tiene un'.1 Guardando Datos de Modelos Relacionados (hasOne. imaginemos que tenemos una acción en nuestro controlador de usuarios UsersController que maneja el guardado de un nuevo usuario y su perfil correspondiente. Entrada y Comentario.4. 'tiene varios'. así que lo // añadimos a los datos a grabar y grabamos el Perfil $this->data['Perfil']['usuario_id'] = $this->Usuario->id. y 'pertenece a'). Si estás guardando una nueva Entrada y sus Comentarios asociados. cuando trabajamos con asociaciones hasOne. quieres guardar registros de un nuevo Usuario y su Perfil relacionado a la vez ). primero necesitarás guardar el modelo primario o padre. entonces deberías usar ambos modelos. belongsTo) Cuando estamos trabajando con modelos asociados. Para tener una idea de cómo funciona esto. . // El ID del nuevo Usuario está ahora en $this->User->id. todo es cuestión de las claves. hasMany.( [comentario] => Comment 2 [comentario] => 2 ) ) ) 3. } ?> } Como norma general.7. La idea básica es coger la clave de un modelo y ponerla en el campo de clave foránea en el otro. pero otras veces podría simplemente implicar obtener el ID desde un campo oculto de un formulario POSTeado a una acción del controlador. A veces esto puede implica usar el atributo $id de la clase del modelo después de save(). // Como nuestro "Usuario hasOne Perfil". podemos acceder // al modelo Perfil a través del modelo Usuario $this->Usuario->Perfil->save($this->data). es importante tener en cuenta que al guardar los datos de un modelo hay que hacerlo con el correspondiente modelo de CakePHP. <?php function add() { if (!empty($this->data)) { // Podemos guardar los datos de Usuario // deberían estar en: $this->data['Usuario'] $this->Usuario->save($this->data). necesitas construir tu formulario tanto para el modelo Compañía como el modelo Cuenta (asumismo que Compañía hasMany Cuenta).nombreusuario'). echo $form->input('Cuenta. Echemos un vistazo a la manera en que hemos nombrado los campos del formulario para el modelo Cuenta. saveAll provee de soporte transaccional para asegurar la integridad de los datos en tu base de datos (p. echo echo echo echo $form->create(Compañía.php podemos crear una acción add(): function add() { if(!empty($this->data)) { $this->Compañia->saveAll($this->data.0.0. si un modelo falla en la grabación.nombreCampo para el modelo asociado. Veamos cómo podemos usar saveAll() para grabar modelos de Compañía (utilizamos este nombre incorrecto por motivos didácticos) y Cuenta al mismo tiempo. $form->input('Compañía. Si Compañía es nuestro modelo principal. Si la asociación entre los modelos es hasOne. Cuenta) llegue en un formado específico. Primero.0.nombre'. echo $form->end('Añadir'). el cual te permite validar y grabar múltiples modelos de golpe. echo $form->input('Cuenta.nombre'. Ahora. $form->input('Compañía. array('action'=>'add')). Recuerda que las tablas MyISAM no soportan transacciones. saveAll esperará que los datos de los modelos relacionados (en este caso. $form->input('Compañía. CakePHP también ofrece el método muy útil saveAll.nombreCampo es exactamente lo que necesitamos. necesitarás usar la notación NombreModelo. array('label'=>'Nombre de compañía')). El nombrado de campos de arriba es necesario para la asociación hasMany. tus tablas han de usar el mecanismo InnoDB. Para que las transacciones funcionen correctametne en MySQL.email').Para complementar el enfoque básico usado arriba. echo $form->input('Cuenta. los otros modelos tampoco serán grabados). Además. y teniendo Cuenta.0. en nuestro compañias_controler.ej. array('label'=>'Nombre de cuenta')).localización').descripción'). } } . array('validate'=>'first')). 7.image_comment_count BlogEntry BlogEntryComment blog_entries. depending on how you look at it) the counter value.4. Una cosa rápida que comentar aquí es el uso de array('validate'=>'first'): esa opción asegurará que ambos modelos son validados.blog_entry_comment_count Once you have added the counter field you are good to go. Ahora nuestros modelos Compañía y Cuenta serán validados y grabados al mismo tiempo.1 counterCache . my_model_count Let's say you have a model called ImageComment and a model called Image. } From now on. class Image extends AppModel { var $belongsTo = array( 'ImageAlbum' => array('counterCache' => true) ). the number within image_count is adjusted automatically. you would add a new INT-field to the image table and name it image_comment_count. Activate counter-cache in your association by adding a counterCache key and set the value to true. Using our Image model example.image_count Image ImageComment images. It allows you to specify a simple condition which tells the model when to update (or when not to. The name of the field consists of the singular model name followed by a underscore and the word "count".1. You can also specify counterScope.Esto es todo para ello. the model itself tracks any addition/deleting towards the associated $hasMany model and increases/decreases a dedicated integer field within the parent model table. Instead of counting the records manually via find('count').Cache your count() This function helps you cache the count of related data. 3. every time you add or remove a Image associated to ImageAlbum. Here are some more examples: Model Associated Model Example User Image users. we can specify it like so: . echo $form->input('Receta.nombre').4. puedes ver el campo oculto Receta. El formulario más simple debería parecerse al algo como esto (asumimos que $receta_id ya está establecido a algo): <?php echo $form->create('Etiqueta').class Image extends AppModel { var $belongsTo = array( 'ImageAlbum' => array( 'counterCache' => true. . echo $form->end('Añadir etiqueta').id'. } 3. La acción del controlador que se encarga de guardar este formulario es muy simple: function add() { // Graba la asociación if ($this->Etiqueta->save($this->data)) { // Hacer algo si todo fue bien } } Y de esa manera. cuyo ID estaba en $this->data['Receta']['id']. belongsTo y hasMany es bastante simple: simplemente rellenas el campo de clave foránea con el ID del modelo asociado.7. 'counterScope' => array('Image.id cuyo valor se establece al ID de la receta a la que queremos enlazar la etiqueta. Una vez que está hecho. $receta_id)). Con HABTM (Has And Belongs To Many). simplemente llamas al método save() del modelo y todo queda enlazado correctamente. ?> array('type'=>'hidden'. Construiremos un formulario que crea una nueva etiqueta y la asocia al vuelo con alguna receta. necesitas establecer el ID del modelo asociado en tu array de datos. 'value' => En este ejemplo.2 Guardando Datos de Modelos Relacionados (HABTM) Grabar modelos que están asociados por hasOne.active' => 1) // only count if "Image" is active = 1 )). nuestra nueva Etiqueta es creada y asociada con Receta. echo $form->input('Etiqueta. definir y utilizar asociaciones entre modelos en CakePHP. la formá más común de .5. las revisiones tienen un único autor. Mientras que los datos pueden provenir de una variedad de orígenes. El propósito de esta sección es mostrarte cómo diseñar. los registros Receta permanecerán después de que el Usuario haya sido borrado. En CakePHP. 3.2 deleteAll deleteAll(mixed $condiciones. El array $condiciones debería ser pasado como un fragmento SQL o array. $cascada = true) De la misma manera que del() y remove().7.5. El definir la manera en que funcionan estas relaciones te permite acceder a tus datos de manera intuitiva y potente. Borra el registro identificado por $id. • si $cascada está establecido a false. una receta puede tener varias revisiones. cuando se borra un registro Usuario que está ligado a varios registros Receta: • si $cascada está establecido a true.6 Asociaciones: Enlazando Modelos Una de las características más potentes de CakePHP es la habilidad para enlazar el mapeado relacional proporcionado por el modelo.3. los enlaces entre modelos son manejados mediante asociaciones. boolean $cascada = true).7. excepto que deleteAll() borra todos los registros que cumplen las condiciones dadas.1 del del(int $id = null. Por ejemplo. Definir relaciones entre diferentes objetos en tu aplicación debería ser un proceso natural. Por defecto. los registros Receta relacionados también son borrados si el valor de dependent (ver la sección hasMany) en el modelo está establecida a true. también borra los registros dependientes del registro especificado a ser borrado. en una base de datos de recetas. y los autores pueden tener varias recetas.7. Por ejemplo.7. 3.5 Borrando Datos 3. Los usuarios en un sistema pueden tener uno a muchos hasMany ("tiene muchos") múltiples recetas. pero puede ser tan completa como un array multidimensional usado para definir asociaciones concretas. } ?> En el ejemplo de arriba. "tiene muchos". belongsTo y hasAndBelongsToMany (HABTM). "pertenece a" y "tiene y pertenece a muchos". Este es un identificador para la relación y puede ser cualquier cosa que escojas. la primera instancia de la palabra 'Receta' es lo que se llama un 'Alias'.1 Tipos de Relaciones Los cuatro tipos de relaciones en CakePHP son: hasOne. La variable de clase puede. De todos modos. 'order' => 'Receta. La mayoría de cosas que cubre esta sección estará en ese contexto. 3. ver Plugin Models. a muchas muchos a muchos") etiquetas. 'conditions' => array('Receta. los alias han de ser únicos dentro de un modelo dado y en ambas partes de una relación belongsTo/hasMany o belongsTo/hasOne. muchos a hasAndBelongsToMany ("tiene y pertenece Las recetas tienen. ser tan simple como una cadena de caracteres. respectivamente.7. muchos a uno belongsTo ("pertenece a") Una receta pertenece a un usuario. escogerás el mismo nombre que la clase que referencia.created DESC' ) ). Escoger nombres no únicos para alias puede causar comportamiento inesperados. hasMany. Normalmente.almacenamiento en aplicaciones web es una base de datos relacional. . y pertenecen. var $hasMany = array( 'Receta' => array( 'className' => 'Receta'. <?php class Usuario extends AppModel { var $name = 'Usuario'. Para obtener información sobre asociaciones con modelos de Plugin. a veces.aprobada' => '1'). var $hasOne = 'Pefil'. Relación uno a uno Tipo de Asociación hasOne ("tiene un") Ejemplo Un usuario tiene un perfil. Las asociaciones son definidas creando una variable de clase nombrada tras la asociación que estás definiendo.6. "tiene un". La manera más simple es establecer el atributo $hasOne a una cadena de caracteres conteniendo el nombre de la clase del modelo asociado. var $hasOne = array( 'Perfil' => array( 'className' => 'Perfil'.6. puedes definir tus asociaciones utilizando sintaxis de arrays.doctor_id El archivo del modelo Usuario será grabado en /app/models/usuario. podrías desear limitar la asociación para incluir sólo ciertos registros. Para definir la asociación 'Usuario hasOne Perfil'.7. la tabla 'perfiles' contendrá un campo llamado usuario_id. Si necesitas más control. 'conditions' => array('Perfil. como hemos hecho arriba. Recuerda tener un modelo Perfil en /app/models/perfil. una tabla ha de contener una clave foránea que apunte a un registro en la otra. necesitas establecer las claves de tus tablas de base de datos correctamente. o la asociación no funcionará. <?php class Usuario extends AppModel { var $name = 'Usuario'. añade la propiedad $hasOne a la clase del modelo.php. En este caso.usuario_id mentores. var $hasOne = 'Perfil'. Por ejemplo.3. Para que funcione una relación hasOne correctamente.2 hasOne Configuremos un modelo Usuario con una relación hasOne con un modelo Perfil. Primero. } ?> Hay dos manera de describir esta relación en tus archivos del modelo. 'dependent' => true ) ).publicado' => '1'). El patrón básico es: hasOne: el otro modelo contiene la clave foránea. <?php class Usuario extends AppModel { var $name = 'Usuario'. Relación Manzana hasOne Plátano Usuario hasOne Perfil Doctor hasOne Mentor Esquema plananos. } ?> .php.manzana_id perfiles. las operaciones de búsqueda en el modelo usuario traerán también el registro Perfil relacionado si existe: // Resultados de ejemplo de una llamada a $this->Usuario->find() Array ( [Usuario] => Array ( [id] => 121 [name] => Gwoo the Kungwoo [created] => 2007-05-01 10:31:01 ) [Perfil] => Array ( [id] => 12 [user_id] => 121 [habilidad] => Hornear Pasteles [created] => 2007-05-01 10:31:01 ) ) . El valor por defecto para esta clave es el nombre en singular del modelo actual. En el ejemplo de arriba. • foreignKey: el nombre de la clave foránea que se encuentra en el otro modelo. y el método delete() del modelo es llamado con el parámetro $cascada con valor true. Una vez que esta asociación ha sido definida. • conditions: Un fragmento SQL usado para filtrar registros del modelo relacionado. • fields: Una lista de campos a ser devueltos cuando se traen los datos del modelo asociado. debería ser por defecto 'usuario_id'. Es buena práctica usar nombres de modelos en los fragmentos SQL: 'Perfil. En este caso lo ponemos a true de manera que borrando un Usuario también borrará su Perfil asociado. la clave className debería ser igual a 'Perfil'. • dependent: Cuando la clave dependent se establece a true. los registros del modelo asociado también son borrados. Por defecto devuelve todos los campos. seguido del sufijo '_id'. Esto es especialmente útil si necesitas definir múltiples relaciones hasOne.Las claves posibles para los arrays de asociaciones hasOne incluyen: • className: el nombre de la clase del modelo que está siendo asociado al modelo actual. si estás definiendo una relación 'Usuario hasOne Perfil'.aprobado = 1' siempre es mejor que simplemente 'aprobado = 1'. 3 belongsTo Ahora que tenemos acceso a los datos de Perfil desde el modelo Usuario. var $belongsTo = array( 'Usuario' => array( 'className' => 'Usuario'.3. Podemos definir la asociación belongsTo en nuestro modelo Perfil en /app/models/perfil. var $belongsTo = 'Usuario'. } ?> . sigue estas convenciones: belongsTo: el modelo actual contiene la clave foránea.usuarios_id mentores.7.php usando la sintaxis de cadena de caracteres así: <?php class Perfil extends AppModel { var $name = 'Perfil'.manzana_id perfiles. } ?> También podemos definir una relación más específica usando sintaxis de arrays: <?php class Perfil extends AppModel { var $name = 'Perfil'. definamos la asociación belongsTo (perteneceA) en el modelo Perfil para tener acceso a los datos de Usario relacionados.6. La asociación belongsTo es un complemento natural a las asociaciones hasOne (tieneUn) y hasMany (tieneMuchos): nos permite ver los datos de la otra dirección. Relación Platano belongsTo Manzana Perfil belongsTo Usuario Mentor belongsTo Doctor Esquema platanos. A la hora de establecer las claves de las tablas de tu base de datos para una relación belongsTo. "perteneceA" (belongsTo) el otro modelo (tabla). 'foreignKey' => 'usuario_id' ) ).doctores_id Si un modelo (tabla) contiene una clave foránea. Una vez que esta asociación ha sido definida. Array ( [Perfil] => Array ( [id] => 12 [usuario_id] => 121 [habilidad] => Baking Cakes [created] => 2007-05-01 10:31:01 ) [Usuario] => Array ( [id] => 121 [name] => Gwoo the Kungwoo [created] => 2007-05-01 10:31:01 ) ) . • fields: lista de campos a ser recuperados cuando los datos del modelo asociado se traen de la base de datos. el modelo asociado automáticamente incrementará o decrementará el campo '[nombre_modelo_en_singular]_count' de la tabla foránea siempre que hagas un save() o delete() (ver counterCache). Es buena práctica usar el nombre de los modelos en los fragmentos SQL: 'Usuario.Claves posibles para los arrays de la asociación belongsTo son: • className: el nombre de la clase del modelo que se está asociando al modelo actual. El valor por defecto de esta clave es el nombre en singular del otro modelo (separado por guiones de subrayado) con el sufijo '_id'. • conditions: el fragmento SQL filtra los registros del modelo relacionado. • foreignKey: el nombre de la clave foránea que se encuentra en el modelo actual. • counterCache: (booleano) si se establece a true. Si estás definiendo una relación 'Perfil belongsTo Usuario'. las operaciones de búsqueda en el modelo Perfil también traerán el registro de Usuario relacionado si existe: // Resultados de ejemplo de la llamada a $this->Perfil->find().activo = 1' siempre es mejor que simplemente 'activo = 1'. Esto es especialmente útil si necesitas definir múltiples relaciones belongsTo. El valor en el campo contador representa el número de filas relacionadas. la clave className ha de tener el valor 'Usuario'. Por defecto devuelve todos los campos. Si estás definiendo una relación 'Usuario hasMany Comentario'. sigue estas convenciones: hasMany: el otro modelo contiene la clave foránea.6. 'foreignKey' => 'usuario_id'.producto_id Podemos definir la asociación hasMany en nuestro modelo Usuario en /app/models/usuario.4 hasMany Siguiente paso: definiendo una asociación "Usuario hasMany Comentario".3. 'dependent'=> true ) ).estado' => '1'). var $hasMany = 'Comentario'. 'limit' => '5'. var $hasMany = array( 'Comentario' => array( 'className' => 'Comentario'. A la hora de establecer las claves de las tablas de tu base de datos para una relación hasMany. } ?> También podemos definir una relación más específica usando sintaxis de arrays: <?php class Usuario extends AppModel { var $name = 'Usuario'. Relación Esquema Usuario hasMany Comentario comentarios. 'order' => 'Comentario.usuario_id Cake hasMany Virtud virtudes. Una asociación hasMany (tieneMuchos) nos permitirá traer los comentarios del usuario cuando se trae un registro Usuario. } ?> Las claves posibles para los arrays de la asociación hasMany son: • className: el nombre de la clase del modelo que está siendo relacionado con el modelo actual.7.cake_id Producto hasMany Opcion opciones. .php usando la sintaxis de cadena de caracteres así: <?php class Usuario extends AppModel { var $name = 'Usuario'. 'conditions' => array('Comentario. el valor de clasName ha de ser 'Comentario'.created DESC'. El segundo parámetro del método Modelo->delete() ha de establecerse a true para que ocurra un borrado recursivo.activo = 1' siempre es mejor que simplemente 'activo = 1'. El valor por defecto para esta clave es el nombre en singular del otro modelo (separado por guiones de subrayado). Por defecto devuelve todos los campos. Esto debería ser usado en situaciones que requieren unos resultados muy personalizados. Esto es especialmente útil si necesitas definir múltiples relaciones hasMany.• foreignKey: el nombre de la clave foránea en el otro modelo. • fields: lista de campos a ser recuperados cuando los datos del modelo asociado se traen de la base de datos. • offset: el número de filas asociadas que quieres saltarte (dadas las condiciones y orden actuales) antes de traer las filas y asociarlas. • finderQuery: Una consulta SQL completa que CakePHP puede usar para traer los registros del modelo asociado. Es buena práctica usar el nombre de los modelos en los fragmentos SQL: 'Usuario. Array ( [Usuario] => Array ( [id] => 121 [name] => Gwoo the Kungwoo [created] => 2007-05-01 10:31:01 ) [Comentario] => Array ( [0] => Array ( [id] => 123 [usuario_id] => 121 [title] => On Gwoo the Kungwoo . con el sufijo '_id'. los registros Comentario serán borrados cuando sus registros Usuario asociados han sido borrados. • limit: el número máximo de filas asociadas que quieres que devuelva. • order: un fragmento SQL que define el orden de las filas asociadas devueltas. las operaciones de búsqueda en el modelo Usuario también traerán los registros Comentario relacionados si existen: // Resultados de ejemplo de llamada a $this->Usuario->find(). • conditions: un fragmento SQL filtra los registros del modelo relacionado. es posible el borrado recursivo del modelo. Una vez que esta asociación ha sido definida. • dependent: Cuando dependent se establece a true. En este ejemplo. 7. Deja de estar disponible para todos. repetidamente. Atando la etiqueta 'Italiano' a la receta 'Gnocci' de mi abuela no 'acapara' la etiqueta. Ya estás versado en tres de las asociaciones que tratan la mayoría de las relaciones de objetos. Esta asociación es usada cuando tienes dos modelos que necesitas unir.6. también puedo etiquetar con 'Italiano' mis 'Espaguettis a la barbacoa con miel glaseada".) [1] => Array ( [id] => 123 [usuario_id] => 121 [title] => More on Gwoo [cuerpo] => But what of the ‘Nut? [created] => 2006-05-01 10:41:01 ) ) ) [cuerpo] => The Kungwooness is not so Gwooish [created] => 2006-05-01 10:31:01 Algo a recordar es que necesitarás la asociación complementaria 'Comentario belongsTo Usuario' para obtener los datos en ambas direcciones. El . en orden alfabético. Andando. vamos a unir nuestro modelo Receta con un modelo Etiqueta usando HABTM. Añadir la asociación 'Comentario belongsTo Usuario' en el modelo comentario te permite obtener los datos de Usuario desde el modelo Comentario. Por ejemplo.5 hasAndBelongsToMany (HABTM) Perfecto. un comentario está sólo enlazado a un usuario específico. o HABTM. Necesitaremos establecer una tabla extra en la base de datos para manejar las asociaciones HABTM. muchas veces. de muchas maneras distintas. Los enlaces entre objetos asociados mediante hasMany son exclusivos. En este punto puedes llamarte "profesional de asociaciones del modelo de CakePHP". Lo que hemos esbozado en esta sección te permite obtener datos de Comentario desde Usuario. y separados por un guión de subrayado ( _ ). Tratemos el último tipo de relación: hasAndBelongsToMany (tieneYPerteneceAMuchos). 3. La principal diferencia entre hasMany y HABTM es que un enlace entre modelos en HABTM no es exclusivo. El nombre de esta nueva tabla de unión necesita incluir los nombres de ambos modelos involucrados en plural. Si mi 'Usuario hasMany Comentarios'. completando la conexión y permitiendo el flujo de la información desde ambas perspectivas del modelo. => ''.receta_id. cada uno clave foránea (que deberían ser enteros) apuntando a ambas claves primarias de los modelos involucrados. El valor por defecto para esta clave es el nombre en singular. => 'etiqueta_id'. => ''. => ''.foo_id. • foreignKey: el nombre de la clave foránea que se encuentra en el modelo actual. separado por guiones de subrayado (_). cakes_fans. Esto es especialmente útil si necesitas definir múltiples relaciones HABTM. del modelo actual .cake_id. podemos definir las asociaciones HABTM en los ficheros del modelo. HABTM necesita una tabla de unión separada que incluya los nombres de ambos modelos. => ''. etiquetas_recetas. => ''. etiquetas_recetas. => 'etiquetas_recetas'. } ?> => 'Etiqueta'.etiqueta_id Cake HABTM Fan id. bars_foos.esquema de la tabla debería contener como mínimo dos campos.fan_id Foo HABTM Bar id. => ''. => 'receta_id'. • joinTable: el nombre de la tabla de unión usuada en esta asociación (si si la tabla actual no se adhiere a la convención de nombrado para tablas de unión HABTM). bars_foos.bar_id Los nombres de las tablas están. Relación Esquema Receta HABTM Etiqueta id. cakes_fans. => true. => '' Claves posibles para los arrays de asociaciones HABTM son: • className: el nombre de la clase del modelo que se está asociando al modelo actual. Si estás definiendo una relación 'Usuario hasAndBelongsToMany Comentarios'. className debería ser igual a 'Comentario'. var $hasAndBelongsToMany = array( 'Etiqueta' => array('className' 'joinTable' 'foreignKey' 'associationForeignKey' 'with' 'conditions' 'order' 'limit' 'unique' 'finderQuery' 'deleteQuery' 'insertQuery' ) ). en orden alfabético. por convención. Vamos a saltar directamente a la sintaxis de arrays esta vez: <?php class Receta extends AppModel { var $name = 'Receta'. Una vez que esta nueva tabla ha sido creada. estado = 1' siempre es preferible a simplemente 'estado = 1'. Una vez que esta asociación ha sido definida. Usando esta clave puedes sustituir este nombre por defecto. El modelo de la tabla de unión puede ser usado como cualquier modelo 'regular' para acceder a la tabla de unión directamente • conditions: fragmento SQL usado para filtrar registros del modelo relacionado. Esto es especialmente útil si necesitas definir múltiples relaciones HABTM. • with: define el nombre del modelo para la tabla de unión. se llamaría EtiquetaReceta. Por defecto. deleteQuery. • associationForeignKey: el nombre de la clave foránea que se encuentra en el otro modelo. Usando el ejemplo de arriba. Esto debería ser usado en situaciones que requieren resultados muy personalizados. Array ( [Receta] => Array ( [id] => 2745 [name] => Bombas de Cholocate con Azúcar Glaseada . Así. borrar o crear nuevos registros del modelo asociado. El valor por defecto para esta clave es el nombre en singulas. Es buena práctica usar nombres de modelos en los fragmentos SQL: 'Comentario. CakePHP autocreará un modelo por ti.con el sufijo '_id'. • order: fragmento SQL que define el orden de las filas asociadas devueltas. • fields: lista de campos a ser devueltos cuando los datos del modelo asociado son traídos. • limit: el número máximo de filas asociadas que deseas que sean devueltas. insertQuery: una consulta SQL completa que CakePHP puede usar para buscar. • unique: si tiene el valor true (valor por defecto) Cake borrará primero los registros de relación existentes en la tabla de claves foráneas antes de insertar nuevas filas. del modelo actual con el sufijo '_id'. cuando se actualiza un registro. separado por guiones de subrayado (_). las asociaciones existentes deberán ser pasadas de nuevo durante las actualizaciones. las operaciones de búsqueda en el modelo Receta también devolverán los registros Etiqueta relacionados si existen: // Resultados de ejemplo de una llamada a $this->Receta->find(). • offset: el número de filas asociadas que omitir (dadas las condiciones actuales y orden) antes de buscar y asociar. • finderQuery. Devuelve todos los campos por defecto. ) [Etiqueta] => Array ( [0] => Array ( [id] => 123 [name] => Desayuno ) [1] => Array ( [id] => 124 [name] => Postre ) [2] => Array ( [id] => 125 [name] => Enfermedad del Corazón ) ) ) [created] => 2007-05-01 10:31:01 [usuario_id] => 2346 Recuerda definir una asociación HABTM en el modelo Etiqueta si quieres traer datos de Receta cuando uses el modelo Etiqueta. También es posible ejecutar consultas de búsqueda personalizadas basadas en relaciones HABTM.name'=>'Postr e') ) ) ) ). // Datos devueltos Array ( 0 => Array { [Receta] => Array ( [id] => 2745 [name] => Bombas de Cholocate con Azúcar Glaseada [created] => 2007-05-01 10:31:01 . digamos que queremos obtener todas las Recetas con la etiqueta 'Postre'. $this->Receta->find('all'). Considera los ejemplos siguientes: Asumiendo la misma estructura en el ejemplo de arriba (Receta HABTM Etiqueta). una manera potencial (pero errónea) de conseguirlo sería aplicar una condición a la misma asociación: $this->Receta->bindModel(array( 'hasAndBelongsToMany' => array( 'Etiqueta' => array( 'conditions'=>array('Etiqueta. lo que nos dará también todas las Recetas asociadas. $this->Receta->bindModel(array('hasOne' => array('EtiquetaReceta'))).etiqueta_id'= >124) // id de Postre )). array( 'fields' => array('Receta. array('conditions'=>array('Etiqueta.} ) 1 => Array { [Receta] => Array ( [id] => 2745 [name] => Pasteles de Cangrejo [created] => 2008-05-01 10:31:01 [usuario_id] => 2349 ) [Etiqueta] => Array ( } } [usuario_id] => 2346 ) [Etiqueta] => Array ( [0] => Array ( [id] => 124 [name] => Postre ) ) Notar que este ejemplo devuelve TODAS las recetas pero sólo la etiqueta 'Postre'. $this->Receta->find('all'. para buscar por un ID dado. Una opción es buscar en el modelo Etiqueta (en vez de Receta). por ejemplo: $this->Receta->bindModel( array( 'hasOne' => array( . $this->Receta->Tag->find('all'. hay diversas maneras de hacerlo. También es posible crear una asociación exótica con el propósito de crear tantas uniones como necesarias para permitir el filtrado. 'conditions'=>array('EtiquetaReceta.name'=>'Postre'))). Podríamos también usar el modelo de tabla de unión (que CakePHP nos provee). Para conseguir nuestro objetivo adecuadamente.*'). id') ) ).name'=>'Postre' ) )). .id = EtiquetaReceta. 'conditions'=>array('EtiquetaReceta.*'). array( 'fields' => array('Receta. ) ) Ambos devolverán los siguientes datos: // Datos devueltos Array ( 0 => Array { [Receta] => Array ( [id] => 2745 [name] => Bombas de Cholocate con Azúcar Glaseada [created] => 2007-05-01 10:31:01 [usuario_id] => 2346 ) [Etiqueta] => Array ( [0] => Array ( [id] => 123 [name] => Desayuno ) [1] => Array ( [id] => 124 [name] => Postre ) [2] => Array ( [id] => 125 [name] => Enfermedad del corazón ) ) } Para más información sobre asociaciones de modelo ligadas al vuelo mira Creando y Destruyendo Asociaciones al Vuelo Mezcla y encaja técnicas para conseguir tu objetivo específico. 'EtiquetaFiltro' => array( 'className' => 'Tag'.'EtiquetaReceta'. 'conditions' => array('EtiquetaFiltro. $this->Receta->find('all'. 'foreignKey' => false. mirar la sección del manual sobre comportamientos empotrados para más información. Esta creación y destrucción de asociaciones se realiza usando los métodos del modelo de CakePHP bindModel() y unbindModel().6 Creando y Destruyendo Asociaciones al Vuelo Algunas veces es necesario crear y destruir asociaciones del modelo al vuelo. var $hasMany = array( 'Seguidor' => array( 'className' => 'Seguidor'.7.3. Por motivos demostrativos. También hay un comportamiento muy útil llamado 'Containable'. usemos unbindModel() para eliminar esa asociación en una acción de un controlador . Empezaremos con dos modelos: <?php class Lider extends AppModel { var $name = 'Lider'.rango' ) ). Esto puede ser por varias razones: • Quieres reducir la cantidad de datos asociados buscados. Como puedes ver arriba. Establezcamos unos pocos modelos para que podamos ver cómo funcionan bindModel() y unbindModel(). • Deseas cambiar la manera en que la asociación está definida para ordenar o filtar los datos asociados. en el LideresController podemos usar el método find() en el modelo Lider para obtener un lider y sus seguidores asociados.6. 'order' => 'Seguidor. } ?> Ahora. el array de asociación en el modelo Lider define una relación 'Lider hasMany Seguidores'. pero todas tus asociaciones están en el primer nivel de recursión. } ?> <?php class Seguidor extends AppModel { var $name = 'Seguidor'. function algunaAccion() { // Esto obtiene Lideres. Esta función aparece en LiderController: function otraAccion() { // No hay 'Lider hasMany Principio' en // el fichero de modelo lider. Si el segundo parámetro ha sido establecido a false. // Ahora usar una funcion find devolverá // Lideres. sólo para la siguiente operación de búsqueda). añadamos otra. // Hemos uado findAll() tras unbindModel(). // NOTE: unbindModel sólo afecta la siguiente función // function. $this->Lider->findAll(). excepto por la declaración var $name. $this->Lider->unbindModel( array('hasMany' => array('Seguidor')) ). } Eliminar o añadir asociaciones usando bind. y sus Seguidores asociados $this->Lider->findAll(). la unión se mantiene para el resto de la petición. // así que esto obtendrá Lideres con Seguidores asociados // una vez más. Aquí está el patrón básico de uso para unbindModel(): $this->Modelo->unbindModel( array('tipoAsociacion' => array('nombreDeClaseDelModeloAsociado')) ). Asociemos algunos Principios a nuestro Lider al vuelo (pero recuerda. Ahora que hemos eliminado satisfactoriamente una asociación al vuelo. asi que una búsqueda // aquí sólo obtiene Lideres..php. // Eliminemos el hasMany. El fichero del modelo para nuestro modelo Principio está vacío. Nuestro Lider 'sin todavía' principios necesita algunos Principios asociados. $this->Lider->findAll().. Una llamada adicional a find usará la // información de la asociación configurada. sin Seguidores $this->Lider->findAll().. // Usemod bindModel() para añadir una nueva asociación // al modelo Lider: $this->Lider->bindModel( array('hasMany' => array( 'Principio' => array( 'className' => 'Principio' ) .y unbindModel() sólo funciona para la operación del modelo next() a menos que el segundo parámetro haya sido establecido a false.. 'foreignKey' => 'usuario_id' ).) ).7 Múltiples relaciones al mismo modelo Hay casos en los que un Modelo tiene más de una relación a otro Modelo. El uso básico para bindModel() es la encapsulación de un array normal de asociación dentro de un array cuya clave es nombrada tras el tipo de asociación que estás tratando de crear: $this->Modelo->bindModel( array('nombreAsociacion' => array( 'nombreDeClaseDelModeloAsociado' => array( // claves de asociacion normales van aquí. Tu Modelo Mensaje luciría así:: <?php class Mensaje extends AppModel { var $name = 'Mensaje'. } ?> . ) ) ) ). // podemos usar una función de búsqueda para obtener // Lideres con sus principios asociados: $this->Lider->findAll().. var $belongsTo = array( 'Emisor' => array( 'className' => 'Usuario'. 'foreignKey' => 'receptor_id' ) ). A pesar de que el nuevo modelo unido no necesita ningún tipo de asociación en la definición de su fichero de modelo. Una relación con el usuario que envía el mensaje y una segunda relación con el usuario que recibe el mensaje.6. Por ejemplo podrías tener un Modelo Mensaje que tiene dos relaciones al Modelo Usuario.7. La tabla mensaje tendrá el campo usuario_id. todavía necesitará tener la clave correcta para que la nueva asociación funcione correctamente. pero tendrá además un campo receptor_id. 'Receptor' => array( 'className' => 'Usuario'.. Ahí lo tienes. 3. ) } // Ahora que hemos asociado correctamente. ) ) ). $Item->find('all'.Receptor es un alias para el Modelo Usuario. For example: $options['joins'] = array( array('table' => 'channels'. In CakePHP some associations (belongsTo and hasOne) performs automatic joins to retrieve data. But this is not the case with hasMany and hasAndBelongsToMany associations.channel_id'. Here is where forcing joins comes to the rescue. $options).8 Joining tables In SQL you can combine related tables using the JOIN statement. 'type' => 'LEFT'. Ahora veamos como se vería el Modelo Usuario. This allows you to perform complex searches across multiples tables (i. a model called Item is left joined to the channels table.6. adding a 'joins' key to the $options array. 'conditions' => array( 'Channel. To force a join between tables you need to use the "modern" syntax for Model::find(). 'foreignKey' => 'receptor_id' ) ). 'alias' => 'Channel'.7. . } ?> 3. You only have to define the necessary joins to combine tables and get the desired results for your query. <?php class Usuario extends AppModel { var $name = 'Usuario'. so you can issue queries to retrieve models based on data in the related one. 'MensajeRecibido' => array( 'className' => 'Mensaje'. 'foreignKey' => 'usuario_id' ).e: search posts given several tags). Note that the 'join' arrays are not keyed. var $hasMany = array( 'MensajeEnviado' => array( 'className' => 'Mensaje'. You can alias the table with the Model name. so the retrieved data complies with the CakePHP data structure. In the above example.id = Item. The keys that define the join are the following: • table: The table for the join. • alias: An alias to the table. The name of the model associated with the table is the best bet. • type: The type of join: inner, left or right. • conditions: The conditions to perform the join. With joins, you could add conditions based on Related model fields: $options['joins'] = array( array('table' => 'channels', 'alias' => 'Channel', 'type' => 'LEFT', 'conditions' => array( 'Channel.id = Item.channel_id', ) ) ); $options['conditions'] = array( 'Channel.private' => 1 ); $privateItems = $Item->find('all', $options); You could perform several joins as needed in hasBelongsToMany: Suppose a Book hasAndBelongsToMany Tag association. This relation uses a books_tags table as join table, so you need to join the books table to the books_tags table, and this with the tags table: $options['joins'] = array( array('table' => 'books_tags', 'alias' => 'BooksTag', 'type' => 'inner', 'conditions' => array( 'Books.id = BooksTag.books_id' ) ), array('table' => 'tags', 'alias' => 'Tag', 'type' => 'inner', 'conditions' => array( 'BooksTag.tag_id = Tag.id' ) ) ); $options['conditions'] = array( 'Tag.tag' => 'Novel' ); $books = $Book->find('all', $options); Using joins with Containable behavior could lead to some SQL errors (duplicate tables), so you need to use the joins method as an alternative for Containable if your main goal is to perform searches based on related data. Containable is best suited to restricting the amount of related data brought by a find statement. 3.7.7 Métodos Callback Si necesitas colar alguna lógica justo antes o después de una operación de modelo de CakePHP, utiliza los callbacks del modelo (funciones de retrollamada). Estas funciones pueden ser definidas en clases del modelo (incluido tu AppModel). Asegúrate de mirar el valor de retorno esperado para cada una de estas funciones especiales. 3.7.7.1 beforeFind beforeFind(mixed $datosConsulta) Llamado antes de cualquier operación relacionada con búsquedas. Los datos de consulta $datosConsulta pasados a este callback contienen información sobre la consulta actual: condiciones, campos, etc. Si no deseas que la operación de búsqueda comience (posiblemente basado en una decisión relacionada con las opciones de $datosConsulta), devuelve false. De lo contrario, devuleve $datosConsulta posiblemente modificado, o cualquier cosa que quieras pasar a la búsquea y sus homólogos. Deberías usar este callback para restringir las operaciones de búsqueda basado en el rol de un usuario, o llevar a cabo decisiones de cacheo basadas en la carga actual. 3.7.7.2 afterFind afterFind(array $resultados, bool $primario) Usa este callback para modficar los resultados que han sido devueltos de una operación de búsqueda, o para realizar cualquier otra lógica tras la búsqueda. El parámetro $resultados pasado a este callback contiene los resultados devueltos por la operación de búsqueda del modelo, p.ej. algo como: $resultados = array( 0 => array( 'NombreModelo' => array( 'campo1' => 'valor1', 'campo2' => 'valor2', ), ), ); Los valores devueltos por este callback deberían ser los resulados (posiblemente modificados) de la operación de búsqueda que dispararon este callback. Si $primario es false, el formato de $resultados será un poco diferente de lo que uno debería esperar; en vez del resultado que obtendrías normalmente de una operación de búsqueda, obtendrías esto: $resultados = array( 'campo_1' => 'valor', 'campo_2' => 'valor2' ); El código que espera que $primario sea true probablemente obtedrá un error falta "Cannot use string offset as an array" de PHP si se usa una búsqueda recursiva Abajo se muestra un ejemplo de cómo afterFind puede ser usado para formateo de datos: function afterFind($resultados) { foreach ($resultados as $clave => $valor) { if (isset($valor['Evento']['fechainicio'])) { $resultados[$clave]['Evento']['fechainicio'] = $this>formatoFechaAfterFind($valor['Evento']['fechainicio']); } } return $resultados; } function formatoFechatAfterFind($cadenaFecha) { return date('d-m-Y', strtotime($cadenaFecha)); } 3.7.7.3 beforeValidate beforeValidate() Usa este callback para modificar datos del modelo antes de que sean validados. También puede ser usado para añadir reglas de validación adicionales más complejas usando Model::invalidate(). En este contexto, los datos del modelo son accesibles via $this->data. Esta función también debe devolver true, de lo contrario la ejecución actual de save() será abortada. 3.7.7.4 beforeSave beforeSave() Sitúa cualquier lógica de antes de grabar en esta función. Esta función se ejecuta inmediatamente después de que los datos del modelo han sido satisfactoriamente validados, pero justo antes de que los datos sean grabados. Esta función debería también devolver true si deseas que continúe la operación de grabado. Este callback es especialmente útil para cualquier lógica de tratamiento de datos que necesita ocurrir antes de que tus datos sean almacenados. Si tu mecanismo de almacenamiento necesita datos en un formato específico, accede a ellos mediante $this->data y modifícalos. Abajo se muestra un ejemplo de cómo beforeSave puede ser usado para conversión de fechas. El código en el ejemplo es usado para una aplicación con una fechainicio formateada como AAAA-MM-DD en la base de datos y es mostrada como DD-MM-AAAA en la aplicación. Por supuesto, esto puede ser cambiado muy facilmente. Usa el código siguiente en el modelo apropiado. function beforeSave() { if(!empty($this->data['Evento']['fechainicio']) && !empty($this>data['Evento']['fechafin'])) { $this->data['Evento']['fechainicio'] = $this>formatoFechaBeforeSave($this->data['Evento']['fechainicio']); $this->data['Evento']['fechafin'] = $this>formatoFechaBeforeSave($this->data['Evento']['fechafin']); } return true; } function formatoFechaBeforeSave($cadenaFecha) { return date('Y-m-d', strtotime($cadenaFecha)); // Direction is from } Asegúrate de que beforeSave() devuelve true, o tu grabado fallará. 3.7.7.5 afterSave afterSave(boolean $creado) Si tienes lógica que necesitas que sea ejecutada justo después de cada operación de grabación, colócala en este método callback. El valor de $creado será true si fue creado un nuevo objeto (en vez de una actualización). 3.7.7.6 beforeDelete beforeDelete(boolean $cascada) Coloca en esta función cualquier lógica de antes de borrar. Esta función debería devolver true si deseas que continúe el borrado, y false si quieres que aborte. El valor de $cascada será true si los registros que dependen de este registro también serán borrados. 3.7.7.7 afterDelete afterDelete() Coloca en este método callback cualquier lógica que quieras que sea ejecutada después de cada borrado. 3.7.7.8 onError onError() Callback llamado si ocurre cualquier problema. 3.7.8 Atributos del Modelo Los atributos del modelo te permiten establecer propiedades que pueden redefinir el comportamiento por defecto del modelo. Para una lista completa de los atributos del modelo y sus respectivas descripciones, visita la API del CakePHP. Echa un vistazo a http://api.cakephp.org/1.2/class_model.html. 3.7.8.1 useDbConfig La propiedad useDbConfig es un cadena de caracteres que especifica el nombre de la conexión a la base de datos usada para enlazar tu clase modelo a la tabla de la base de datos relacionada. Puedes estabecer el valor a cualquiera de las conexiones definidas dentro de tu fichero de configuración de tu base de datos. El fichero de configuración de la base de datos se encuentra en /app/config/database.php. La propiedad useDbConfig tiene por defecto la conexión a la base de datos 'default' ( $useDbConfig = 'default'; ) Ejemplo de uso: class Ejemplo extends AppModel { var $useDbConfig = 'alternativo'; } 3.7.8.2 useTable La propiedad $useTable especifica el nombre de la tabla de la base de datos. Por defecto, el modelo usa la forma plural y en minúsculas del nombre de la clase del modelo. Establece este atributo al nombre de una tabla alternativa, o dale el valor false si deseas que el modelo no use una tabla de base de datos. Ejemplo de uso: class Ejemplo extends AppModel { var $useTable = false; // Este modelo no usa una tabla de base de datos } Alternativamente: class Ejemplo extends AppModel { var $useTable = 'exmp'; // Este modelo usa la tabla 'exmp' de la base de datos } 3.7.8.3 tablePrefix El nombre del prefijo de tabla usado para el modelo. El prefijo de tabla se establece inicialmente en el fichero de conexión a la base de datos /app/config/database.php. Por defecto es sin prefijo. Puedes sustituir la configuración por defecto estableciendo el atributo tablePrefix en el modelo. Ejemplo de uso: class Ejemplo extends AppModel { var $tablePrefix = 'otros_'; // buscará la tabla 'otros_ejemplos' } 3.7.8.4 primaryKey Normalmente cada tabla tiene una clave primaria id. Puedes cambiar qué nombre de campo usará el modelo como clave primaria. Esto es común cuando se configura CakePHP para usar una tabla de base de datos ya existente. Ejemplo de uso: class Ejemplo extends AppModel { var $primaryKey = 'ejemplo_id'; // ejemplo_id es el nombre del campo en la base de datos } 3.7.8.5 displayField El atributo displayField ('visualizarCampo') especifica qué campo de la base de datos debería ser usado como etiqueta para el registro. La etiqueta se utiliza en scaffolding y en llamadas find('lista'). El modelo usará por defecto el campo name o title. Por ejemplo, para utilizar el campo nombre_de_usuario: class Ejemplo extends AppModel { var $displayField = 'nombre_de_usuario'; } No se pueden combinar nombres de campos múltiples en un único campo de display (de visualización). Por ejemplo, no puedes especificar array('nombre', visualización. 3.7.8.6 recursive 'apellido') como campo de La propiedad $recursive define la profundidad a la que CakePHP ha de llegar para obtener los datos de modelos asociados mediante los métodos find() y findAll(). Imagina que tu aplicación muestra Grupos que pertenecen a un Dominio que tiene muchos Usuarios que, a su vez, tienen muchos Artículos. Puedes establecer $recursive con diferentes valores basados en la cantidad de datos quieres obtener con una llamada a $this->Grupo->find(): Profundidad -1 0 1 Descripción Cake obtiene sólo los datos de Grupo, no realiza uniones (joins). Cake obtiene datos de Grupo y su Dominio Cake obtiene un Grupo, su Dominio y sus Usuarios asociados 2 Cake obtiene un Grupo, su Dominio, sus Usuarios asociados y los Artículos asociados a los Usuarios No lo establezcas a un valor mayor de lo que necesites. Hacer que CakePHP obtenga datos que no vas a utilizar ralentiza tu aplicacióń innecesariamente. Si deseas combinar $recursive con la funcionalidad de $fields, necesitarás añadir las columnas que contienen las claves foráneas necesarias al array fields manualmente. En el ejemplo de arriba, esto podría significar añadir domain_id. 3.7.8.7 order El criterio de ordenación de datos por defecto para cualquier operación de búsqueda. Algunos valores posibles son: $order $order $order $order $order $order = = = = = = "campo" "Modelo.campo"; "Modelo.campo asc"; "Modelo.campo ASC"; "Modelo.campo DESC"; array("Modelo.campo" => "asc", "Modelo.campo2" => "DESC"); 3.7.8.8 data El contenedor para los datos del modelo que se han obtenido. A pesar de que los datos devueltos por una clase del modelo normalmente se utilizan como los devueltos por una llamada a find(), dentro de un callback del modelo necesitarás acceder a la información almacenadana a través de $data. 3.7.8.9 _schema Contiene metadatos describiendo los campos de tabla de la base de datos del modelo. Cada campo es descrito por: • nombre • tipo (integer, string, datetime, etc.) • null • valor por defecto • longitud 3.7.8.10 validate Este atributo contiene reglas que permiten al modelo realizar decisiones de validación de datos antes de grabar. Las claves nombradas tras los campos contienen expresiones regulares permitiendo al modelo buscar correspondencias. Para más información, mira el capítulo Validación de Datos más adelante en este manual. 3.7.8.11 virtualFields Array of virtual fields this model has. Virtual fields are aliased SQL expressions. Fields added to this property will be read as other fields in a model but will not be saveable. Example usage for MySQL: var $virtualFields = array( 'name' => "CONCAT(User.first_name, ' ', User.last_name)" ); In subsequent find operations, your User results would contain a name key with the result of the concatenation. It is not advisable to create virtual fields with the same names as columns on the database, this can cause SQL errors. For more information on the virtualFields property, its proper usage, as well as limitations, see the section on virtual fields. 3.7.8.12 name Como habrás visto antes en este capítulo, el atributo $name es una característica de compatibilidad para los usuarios de PHP4 y se establece el valor al nombre del modelo. Ejemplo de uso: class Ejemplo extends AppModel { var $name = 'Ejemplo'; } 3.7.8.13 cacheQueries Si se establece a true, los datos obtenidos por el modelo durante una petición son cacheados (cached). Este cacheo es sólo en memoria, y dura sólo el tiempo de duración de la petición. Cualquier petición duplicada de los mismos datos es tratada por la caché. 3.7.9 Métodos Personalizados y Propiedades Aunque las funciones de modelo de CakePHP deberían llevarte donde necesites ir, no olvides que las clases modelos son justamente eso: clases que te permiten escribir tus propios métodos o definir tus propias propiedades. Cualquier operación que maneja la grabación o búsqueda de datos es mejor que esté alojada en tus clases modelo. Este concepto es a menudo referido como "fat model". class Ejemplo extends AppModel { function getReciente() { $condiciones = array( 'created BETWEEN (curdate() - interval 7 day) and (curdate() - interval 0 day))' ); return $this->find('all', compact($condiciones)); } } Ahora, este método getReciente() puede ser usado dentro del controlador. $reciente = $this->Ejemplo->getReciente(); 3.7.9.1 Using virtualFields Virtual fields are a new feature in the Model for CakePHP 1.3. Virtual fields allow you to create arbitrary SQL expressions and assign them as fields in a Model. These fields cannot be saved, but will be treated like other model fields for read operations. They will be indexed under the model's key alongside other model fields. How to create virtual fields Creating virtual fields is easy. In each model you can define a $virtualFields property that contains an array of field => expressions. An example of virtual field definitions would be: var $virtualFields = array( 'name' => 'CONCAT(User.first_name, ' ', User.last_name)' ); In subsequent find operations, your User results would contain a name key with the result of the concatenation. It is not advisable to create virtual fields with the same names as columns on the database, this can cause SQL errors. Using virtual fields Creating virtual fields is straightforward and easy, interacting with virtual fields can be done through a few different methods. Model::hasField() Model::hasField() has been updated so that it can will return true if the model has a virtualField with the correct name. By setting the second parameter of hasField to true, virtualFields will also be checked when checking if a model has a field. Using the example field above, $this->User->hasField('name'); // Will return false, as there is no concrete field called name $this->User->hasField('name', true); // Will return true as there is a virtual field called name Model::isVirtualField() This method can be used to check if a field/column is a virtual field or a concrete field. Will return true if the column is virtual. $this->User->isVirtualField('name'); //true $this->User->isVirtualField('first_nname'); //false Model::getVirtualField() This method can be used to access the SQL expression that comprises a virtual field. If no argument is supplied it will return all virtual fields in a Model. $this->User->getVirtualField('name'); User.last_name)' //returns 'CONCAT(User.first_name, ' ', Model::find() and virtual fields As stated earlier Model::find() will treat virtual fields much like any other field in a model. The value of a virtual field will be placed under the model's key in the resultset. Unlike the behavior of calculated fields in 1.2 $results = $this->User->find('first'); // results contains the following array( 'User' => array( e. " ". 3.10. It is not always useful to have User. User. In each model you can define a $virtualFields property that contains an array of field => expressions. An example of a virtual field definition using MySQL would be: var $virtualFields = array( 'full_name' => 'CONCAT(User. 'full_name' => 'CONCAT(User. //more fields. ). In this case it . you have multiple relations to other tables) this would result in an error. 1. Virtual fields allow you to create arbitrary SQL expressions and assign them as fields in a Model. Controller::paginate() has been updated to allows sorting by virtual fields.1 Creating virtual fields Creating virtual fields is easy.7.last_name)' 3. Pagination and virtual fields Since virtual fields behave much like regular fields when doing find's.first_name || \' \' || User. In subsequent find operations. They will be indexed under the model's key alongside other model fields. These fields cannot be saved. var $virtualFields = array( 2. If you do not follow the convention (i. It is not advisable to create virtual fields with the same names as columns on the database.first_name fully qualified.7. 'name' => 'Mark Story'. 'first_name' => 'Mark'. And with PostgreSQL: Plain Text View var $virtualFields = array( 'name' => 'User.3.last_name)' ). but will be treated like other model fields for read operations.10 Virtual fields Virtual fields are a new feature in the Model for CakePHP 1.first_name.last_name' ). 'last_name' => 'Story'. User.) ). this can cause SQL errors. your User results would contain a name key with the result of the concatenation. 3.first_name. " ". ' '. User. interacting with virtual fields can be done through a few different methods. Using the example field above. Will return true if the column is virtual. 'name' => 'Mark Story'. By setting the second parameter of hasField to true.2 $results = $this->User->find('first'). $this->User->getVirtualField('name'). // Will return true as there is a virtual field called name Model::isVirtualField() This method can be used to check if a field/column is a virtual field or a concrete field. //returns 'CONCAT(User.2 Using virtual fields Creating virtual fields is straightforward and easy. $this->User->hasField('name'). true). The value of a virtual field will be placed under the model's key in the resultset. Unlike the behavior of calculated fields in 1. // Will return false. $this->User->isVirtualField('name'). //more fields. as there is no concrete field called name $this->User->hasField('name'. virtualFields will also be checked when checking if a model has a field. //false Model::getVirtualField() This method can be used to access the SQL expression that comprises a virtual field.may be better to just use first_name || \'\' || last_name without the Model Name. If no argument is supplied it will return all virtual fields in a Model.7. // results contains the following array( 'User' => array( 'first_name' => 'Mark'. Model::hasField() Model::hasField() has been updated so that it can return true if the model has a virtualField with the correct name. 'last_name' => 'Story'. .first_name.last_name)' Model::find() and virtual fields As stated earlier Model::find() will treat virtual fields much like any other field in a model.10. ) ). //true $this->User->isVirtualField('first_name'). 3. This will allow your virtualFields to work for any alias you give a model.3 Virtual fields and model aliases When you are using virtualFields and models with aliases that are not the same as their name.4 Limitations of virtualFields The implementation of virtualFields in 1. 3.$table. Doing so will generally result in an SQL error as the fields are not replaced by the ORM. A common workaround for this implementation issue is to copy virtualFields from one model to another at runtime when you need to access them.10. $this->virtualFields = array( 'name'=>"CONCAT(`{$this->alias}`. } " ".7. Alternatively. This is because it difficult to estimate the depth at which an associated model might be found.' >alias}`.$table=null.`first_name`. like so: public function __construct($id=false.`last_name`)" ). or fields arrays. Controller::paginate() has been updated to allows sorting by virtual fields. %s. If you are using virtualFields in models that have more than one alias it is best to define the virtualFields in your model's constructor function __construct($id = false. First you cannot use virtualFields on associated models for conditions. $this->virtualFields['name'] = sprintf('CONCAT(%s. $ds = null) { parent::__construct($id. $this->alias.first_name. $this->virtualFields['full_name'] = $this->Author->virtualFields['full_name'].Pagination and virtual fields Since virtual fields behave much like regular fields when doing find's. } '. 3.7.10.`{$this- .3 has a few limitations. you can run into problems as virtualFields do not update to reflect the bound alias. $table = null. $this->alias). order. $ds).$ds=null){ parent::__construct($id.last_name)'. $table. using $this>alias.$ds). you can define $virtualFields in your model's constructor. ser extrapolado. podríamos simplemente decirle a nuestro modelo que utilize el TreeBehavior (ArbolComportamiento).7. Muchos registros deberán ser actualizados según las cosas se mueven. To get a model's DataSource from within the model.11 Transactions To perform a transaction. } else { $dataSource->rollback($this). //Perform some tasks if(/*all's well*/) { $dataSource->commit($this). o en términos más formales. Proveyendo una manera simple pero potente manera de extender modelos. $dataSource->begin($this).3. decirle a nuestro modelo que se comporte como un Árbol. All transaction methods must be performed on a model's DataSource object. use: $dataSource = $this->getDataSource().8 Comportamientos Los comportamientos del modelo (Model behaviors) son una manera de organizar parte de la funcionalidad definida en los modelos de CakePHP. Esto es conocido como atar un . insertar y editar filas en una tabla. You can then use the data source to start. añadiendo y migrando nodos en el árbol no es tan simple como borrar. En vez de crear esos métodos de manipulación del árbol en cada base de modelo (para todo modelo que necesita dicha funcionalidad). pero que necesita estar ahí. If a nested transaction is started. Nos permiten separar la lógica que puede no estar relacionada directamente con un modelo. considera un modelo que nos da acceso a una tabla de una base de datos la cual almacena información estructural de un árbol. a commit will return false on the parent transaction. Como ejemplo. 3. or roll back transactions. entonces. los comportamientos nos permiten atar funcionalidad a los modelos definiendo una simple variable de clase. Así es como los comportamientos permiten a los modelos deshacerse de todo el peso extra que no debería ser parte de contrato de negocio que están modelando. Eliminando. commit. } Nested transactions are currently not supported. o al menos es necesario en diferentes modelos y puede. a model's tables must be of a type that supports transactions. Una vez que un comportamiento ha sido especificado. Algunos comportamientos pueden requerir o permitir opciones a ser definidas cuando el comportamiento es atado al modelo. } ?> . cómo utilizar los comportamientos de CakePHP incorporados y cómo crear uno nosotros mismos. 'right' => 'right_node' ) ).comportamiento a un modelo. CakePHP ya incluye comportamientos para estructuras de árbol. } ?> Este ejemplo muestra cómo un modelo de Category puede ser manejado en una estructura de árbol utilizando el comportamiento de árbol (TreeBehavior). En esta sección cubriremos el patrón básico de uso para añadir comportamientos a modelos. children(): $kids = $this->Category->children(). // Utilizar un método de comportamiento. Con sólo una línea de código. var $actsAs = array( 'Tree' => array( 'left' => 'left_node'. var $actsAs = array('Tree').8. nuestro modelo de CakePHP toma todo un nuevo conjunto de métodos que le permiten interactuar con la estructura subyacente. contenido traducido. 3.1 Utilizando Comportamientos Los comportamientos son atados a modelos mediante la variable de clase del modelo $actsAs: <?php class Category extends AppModel { var $name = 'Category'. decimos a nuestro TreeBehavior (comportamiento de árbol) los nombres de los campos left (izquierdo) y right (derecho) en la tabla de base de datos subyacente: <?php class Category extends AppModel { var $name = 'Category'. sin comentar los comportamientos aportados por la comunidad disponibles en CakePHP Bakery. interacción con listas de control de acceso. Aquí. utiliza los métodos añadidos por el comportamiento como si siempre existiesen como parte del modelo original: // Set ID $this->Category->id = 42. Tal vez necesitemos. También necesitaremos saber si nuestro comportamiento está manejando los callbacks del modelo. en cambio. Digamos que en nuestro modelo Category anterior. nuestro modelo Category deba sólo comportarse como un árbol. Eso hará que nuestro modelo Category deje de comportarse como un modelo Translate desde ese momento. por ejemplo.Podemos también atar varios comportamientos a un modelo. el cual está actuando como un modelo Tree y Translate. Sin embargo. No hay razón por la que. necesitamos por alguna razón forzar a que deje de actuar como un modelo Translate: // Desata un comportamiento de nuestro modelo: $this->Category->Behaviors->detach('Translate'). puede ser que necesitemos "desatar" comportamientos de nuestros modelos en tiempo de ejecución. etc. } . var $actsAs = array( 'Tree' => array( 'left' => 'left_node'. le decimos a nuestro modelo que pare de informar sobre dichos callbacks al comportamiento Translate: // Parar de dejar al comportamiento manejar los callbacks de nuestro modelo $this->Category->Behaviors->disable('Translate'). En vez de desatar el comportamiento. simplemente desactivar el comportamiento Translate de actuar sobre operaciones normales del modelo: nuestras búsquedas. Eso significa que nuestros comportamientos serán atados a muestros modelos durante todo el tiempo de vida del modelo. } ?> Hasta el momento hemos estado añadiendo comportamientos a modelos utilizando una variable de la clase. estamos buscando desactivar el comportamiento de actuar sobre nuestros callbacks del modelo de CakePHP. De hecho. 'Translate' ). 'right' => 'right_node' ). y si no. restauramos su habilidad para reaccionar a ellos: // Si nuestro comportamiento no está manejando los callbacks del modelo if (!$this->Category->Behaviors->enabled('Translate')) { // Decirle que comience a hacerlo $this->Category->Behaviors->enable('Translate'). grabados. también puede necesitar soporte de internacionalización: <?php class Category extends AppModel { var $name = 'Category'. For example if you had: class Duck extends AppModel { var $name = 'Duck'.8. Digamos que nuestro modelo Category necesita empezar a comportarse como un modelo Christmas (Navidad). the method signature should look like: . 'montreal'). array('left' => 'new_left_node')). pero sólo en el día de Navidad: // Si hoy es 25 de diciembre if (date('m/d') == '12/25') { // Nuestro modelo necesita comportarse como un modelo Christmas $this->Category->Behaviors->attach('Christmas'). All other supplied parameters are shifted one place to the right.3 Creating behavior methods Behavior methods are automatically available on any model acting as the behavior. } You would be able to call FlyingBehavior methods as if they were methods on your Duck model. // Si el comportamiento "Translate" no está atado al modelo if (!$this->Category->Behaviors->attached('Translate')) { // Obtener la lista de todos los comportamientos atados al modelo $behaviors = $this->Category->Behaviors->attached(). } Podemos también utilizar el método attach() para sobreescribir las opciones de comportamiento: // Cambiaremos una opción de nuestro ya atado comportamiento $this->Category->Behaviors->attach('Tree'. For example $this->Category->fly('toronto'. nos dirá si dicho comportamiento está atado al modelo. Although this method takes two parameters. de cualquier otra manera nos dará una lista de los comportamientos atados. } 3.Tal como podemos desatar completamente un comportamiento de un modelo en tiempo de ejecución.8. También hay un método para obtener la lista de comportamientos atados a un modelo: attached(). var $actsAs = array('Flying').2 Creando Comportamientos Personalizados 3. When creating behavior methods you automatically get passed a reference of the calling model as the first parameter. Si pasamos el nombre de un comportamiento al método. también podemos atar nuevos comportamientos. 5 Creating a behavior callback Model behavior callbacks are defined as simple methods in your behavior class. The return value will be passed on as the results to either the next behavior in the chain or the model's afterFind.4 Behavior callbacks Model Behaviors can define a number of callbacks that are triggered before/after the model callbacks of the same name.8. This parameter is the model that the behavior method was invoked on. Returning an array will augment the query parameters used for the find operation. $to) { // Do some flying. Behavior callbacks allow your behaviors to capture events in attached models and augment the parameters or splice in additional behavior. afterFind(&$model. Much like regular behavior methods. $query) If a behavior's beforeFind return's false it will abort the find().8. $results. .function fly(&$Model. they receive a $Model parameter as the first argument. 3. function beforeFind(&$model. } Keep in mind that methods called in a $this->doIt() fashion from inside a behavior method will not get the $model parameter automatically appended. $from. $primary) You can use the afterFind to augment the results of a find. The available callbacks are: • beforeValidate is fired before a model's beforeValidate • beforeFind is fired before a model's beforeFind • afterFind is fired before a model's afterFind • beforeSave is fired before a model's beforeSave • afterSave is fired before a model's afterSave • beforeDelete is fired after a model's beforeDelete • afterDelete is fired before a model's afterDelete 3. php • dbo_oracle.php • dbo_db2.beforeDelete(&$model.php • dbo_sybase.php • dbo_sqlite. CakePHP usa de forma transparente la datasource correspondiente .php • dbo_postgres. los datos son recuperados de una base de datos relacional.php • dbo_mysql.9 DataSources (fuentes de datos) DataSources son el enlace entre los modelos y la fuente de datos que cada modelo representa. aquí se lista un resumen de los mismos para tu comodidad: • dbo_adodb.php • dbo_odbc. beforeValidate(&$model) You can use beforeValidate to modify a model's validate array or handle any other prevalidation logic.php. Returning false from a beforeValidate callback will abort the validation and cause it to fail. 3. PostgreSQL o MSSQL. afterDelete(&$model) You can use afterDelete to perform clean up operations related to your behavior. CakePHP se distribuye con varias datasources específicas para varias bases de datos (consulta los archivos de clases dbo_* class files en cake/libs/model/datasources/dbo/).php • dbo_firebird.php • dbo_mysqli. como MySQL. En muchos caos. Return true to allow it continue.php Cuando se especifica una configuración de conexión a base de datos en app/config/database. $cascade = true) You can return false from a behavior's beforeDelete to abort the delete.php • dbo_mssql. $fields = array().si estás escribiendo una datasource de sólo-lectura. $values = array()) • delete($model.a la base de datos para todas las operaciones con modelos. en lugar de en el modelo. Si decides crear una datasource RDBMS. $fields = array(). update y/o delete (la signatura y detalles de implementación no son importantes en este momento. $id = null) Es posible también (y muchas veces bastante útil) definir el atributo de clase $_schema dentro la datasource misma.php o dbo_mssql. y serán descritos más tarde). y debería implementar al menos uno de los siguientes métodos: create. $values = array()) • read($model. read. Métodos que deben ser implementados • describe($model) • listSources() • Al menos uno de: • create($model. aunque creas que no sabes nada de datasources. Todas las fuentes de datos indicadas arriba derivan de una clase base DboSource la cual añade alguna lógica común a la mayoría de bases de datos relaciones.php) La mayor parte de la gente. como APIs REST remotas o incluso servidores LDAP. Emparejando una datasource a un modelo podrás utilizar Model::find()/save/() como harías normalmentem y los datos y/o parámetros adecuados serán usados para llamar a esos métodos serán pasados a la propia datasource.1 API básica para DataSources Una datasource puede. está interesada en escribir datasources para fuentes de datos externas. No necesitas implementar más métodos de los necesarios de los descritos arriba . ya las has estado usando desde siempre. donde puedes decidir implementar cualquier prestación que necesites (por ejemplo: opciones para Model::find como procesasr 'conditions'. . Así que eso es lo que vamos a examinar en adelante. 'limit' o incluso tus propios parámetros a medida). no hay razón para implementar create y update. tu mejor apuesta es trabajar a paritr de una de ellas (por ejemeplo: dbo_mysql. Y esto es casi todo lo que hay. Por eso. sin embargo.9. $queryData = array()) • update($model. 3. 'null' => true.2 o superior. 'null' => true. Cake Software Foundation.cakephp.opensource. 'HttpSocket'). (http://www. * * PHP Version 5.php The MIT License */ App::import('Core'. class TwitterSource extends DataSource { protected $_schema = array( 'tweets' => array( 'id' => array( 'type' => 'integer'. Inc. 'text' => array( 'type' => 'string'. 'null' => true. * * @filesource * @copyright Copyright 2009. 'status' => array( 'type' => 'string'. debido al uso de json_decode para procesar los datos en formato JSON.cakefoundation. Inc.org) * @link http://cakephp.php: <?php /** * Twitter DataSource * * Utilizada para leer y escribir en Twitter usando modelos.org) * Copyright 2005-2009. Cake Software Foundation. 'key' => 'primary'. Este ejemplo sólo funcionará sobre PHP 5.org) * * Licensed under The MIT License * Redistributions of files must retain the above copyright notice. 'key' => 'primary'. (http://www. ). 'length' => 140 ).x * * CakePHP(tm) : Rapid Development Framework (http://www.org/licenses/mit-license.cakefoundation. 'length' => 11.org CakePHP(tm) Project * @license http://www. Tendrás que colocar la datasource para Twitter en tu app/models/datasources/twitter_source. .9.3.2 Un ejemplo Lo que sigue es un ejemplo simple de como usar dataSources y HttpSocket para implementar una fuente muy básica de Twitter que nos permita utilizar la API de twitter y enviar nuestras actualizaciones. true). $record['User'] = $record['Tweet']['user']. ) 'key' => 'primary'. parent::__construct($config). $response = json_decode($this->connection->get($url). 'length' => 140 ). $results = array(). } public function create($model. $url . unset($record['Tweet']['user']). } ?> . $results[] = $record. if (isset($result['id']) && is_numeric($result['id'])) { $model->setInsertId($result['id']). $values = array()) { $data = array_combine($fields. } public function read($model. } return false. } public function describe($model) { return $this->_schema['tweets'].). public function __construct($config) { $auth = "{$config['login']}:{$config['password']}". $result = json_decode($result. true).json'. $result = $this->connection->post('/statuses/update. $data). $this->connection = new HttpSocket( "http://{$auth}@twitter. $fields = array(). return true. foreach ($response as $record) { $record = array('Tweet' => $record).com/" ). } } ?> La implementación de tu modelo puede ser tan simple como: <?php class Tweet extends AppModel { public $useDbConfig = 'twitter'.= "{$queryData['conditions']['username']}. } $url = "/statuses/user_timeline/".json". } return $results. $values). $queryData = array()) { if (!isset($queryData['conditions']['username'])) { $queryData['conditions']['username'] = $this>config['login']. } public function listSources() { return array('tweets'). ). guardando una actualización del estatus: <?php $this->Tweet->save(array('status' => 'This is an update')).php se parecerán a estos: <?php var $twitter = array( 'datasource' => 'twitter'. // Encontrar tweets de otros usuario $conditions= array('username' => 'caketest'). ?> Usando los familiares métodos de modelo desde un controlador: <?php // Usará el nombre de usuario definido en $twitter como se mostró arriba: $tweets = $this->Tweet->find('all'). 'login' => 'username'. ?> . nos dará un mensaje de error al efecto aquí.Si no hemos definido nuestro esquema en la propia datasource. Y los ajustes de configuración en tu app/config/database. 'password' => 'password'. compact('conditions')). ?> De forma similar. $otherTweets = $this->Tweet->find('all'. 3. Cualquier cosa que quieras ver en todas tus vistas debería estar situada en un layout. El diseño por defecto de . Los ficheros de vista de CakePHP están escritos en PHP plano y tienen la extensión . Los elementos generalmente son renderizados dentro de vistas. paginar los datos del modelo o servir feeds RSS. y será tratado en este capítulo: • layouts (diseños): ficheros de vista que contienen el código de presentación que se encuentra envolviendo muchas interfaces en tu aplicación. • elements (elementos): trozo de código de vista más pequeño y reutilizable. se encontraría en /app/views/productos/view.10 Vistas 3. los ayudantes en CakePHP pueden ayudarte a construir formularios. normalmente.1 Plantillas de la Vista La capa vista de CakePHP es cómo hablas a tus usuarios.3. Los ficheros de vista se almacenan en /app/views/. el fichero de vista para el la acción view() del controlador Productos.ctp.10. La capa vista en CakePHP puede estar formada por un número diferentes de partes. responder a una aplicación remota mediante SOAP o producir un fichero CSV para un usuario. Cada parte tiene usos diferentes.ctp (CakePHP Template) por defecto . pero tal vez necesites servir datos AMF a un objeto Flash. en una carpeta nombrada tras el controlador que usa los ficheros. Estos ficheros contienen toda la lógica de representación necesaria para obtener los datos recibidos del controlador en un formato que está preparado para la audiencia a la que estás atendiendo. La mayor parte del tiempo tu vista estará mostrando documentos (X)HTML a los navegadores. y nombrada tras la acción a la que corresponde.10. Los ficheros de diseño deberían situarse en /app/views/layouts.2 Layouts Un diseño contiene el código de presentación que envuelve una vista. La mayoría de vistas son 'renderizadas' (presentadas) dentro de un layout (diseño). construir funcionalidad AJAX. Por ejemplo. • helpers (ayudantes): estas clases encapsulan lógica de vista que es necesaria en muchas partes en la capa vista. Además de otras cosas. estate seguro que tu diseño incluye un lugar para $content_for_layout (y opcionalmente. $title_for_layout)..ico" type="image/x-icon"> <!-.. $title_for_layout contiene el título de la página. el código de la vista renderizado por el controlador se coloca dentro del diseño por defecto cuando la página es renderizada.. Para hacer eso. Aquí es donde será colocado el código de la vista.w3.w3.dtd"> <html xmlns="http://www.Si quieres algún tipo de menú que mostrar en todas tus vistas. Es últil para incluir ficheros javascript y CSS de las vistas. .Aquí es donde quiero que se vean mis vistas --> <?php echo $content_for_layout ?> <!-.</div> </body> </html> $scripts_for_layout contiene cualquier fichero externo y scripts incluidos con el ayudante HTML incrustado.org/1999/xhtml"> <head> <title><?php echo $title_for_layout?></title> <link rel="shortcut icon" href="favicon. incluyelo aquí --> <div id="cabecera"> <div id="menu">.0 Transitional//EN" "http://www. Cuando uses $html->css() o $javascript->link() en los ficheros de vista.org/TR/xhtml1/DTD/xhtml1-transitional.Incluir ficheros y scripts externos aquí (Mirar el ayudante HTML para más información --> <?php echo $scripts_for_layout ?> </head> <body> <!-. (Mirar API para más detalles de uso).ctp. $content_for_layout contiene la vista.. Cuando creas un diseño. Una vez que ha sido creado un nuevo diseño. Aquí está un ejemplo de a lo que debería parecerse un diseño por defecto: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1. especifica false en el argumento 'in-line' para colocar el fuente html en $scripts_for_layout.Añadir un pie de página a cada página mostrada --> <div id="pie">. necesitas decirle a CakePHP dónde colocar el código para tus vistas.CakePHP puede ser sustituido creando un nuevo diseño por defecto en /app/views/layouts/default.</div> </div> <!-. Para establecer el título para el diseño. } } ?> Puedes crear tantos diseños como desees: simplemente colócalos en el directorio app/views/layouts e intercambialos dentro de tus acciones del controlador usando la variable $layout del cotrolador. o la función setLayout(). 'Ver Usuarios Activos'). más que una interfaz completa). . } function verImagen() { $this->layout = 'imagen'. <?php class UsuariosController extends AppController { function verActivos() { $this->set('title_for_layout'. estableciendo el valor de la variable $title_for_layout. Existen otros tres diseños: xml. //mostrar la imagen del usuario } } ?> CakePHP ofrece dos diseños comunes (además del diseño por defecto de CakePHP) que puedes usar en tus propias aplicaciones: 'ajax' y 'flash'. si una sección de mi site incluye un espacio menor con banner de publicidad. js y rss en el núcleo como una manera rápida y fácil de servir contenido que no sea text/html. debería crear un nuevo diseño (layout) con el espacio publicitario menor. El diseño Ajax es útil para contruir las respuestas Ajax. Por ejemplo. es un diseño vacío (la mayoría de las llamadas ajax sólo requiren una pocas etiquetas como respuesta. 'Ver Usuarios Activos'). es fácil hacerlo en el controlador. <?php class UsuariosController extends AppController { function verActivos() { $this->set('title_for_layout'. El diseño flash es usado por mensajes mostrados por el método flash() del controlador. especificándolo como el diseño para todas las acciones del controlador haciendo algo como: var $layout = 'publicidad_pequena_pordefecto'. $this->layout = 'publicidad_pequena_pordefecto'. menús extras. this text is very helpful. ?> 3.1 Pasar Variables a un elemento Puedes pasarle datos a un elemento usando el segundo argumento de element(): <?php echo $this->element('helpbox'. //outputs "Oh.3.")). En el ejemplo siguiente. en diseños. todas las variables pasadas estan disponibles como miembros del array de parámetros (de la misma manera que set() en el controlador funciona con los archivos de las vistas). array("helptext" => "Oh. algunas veces en diferentes lugares del diseño.3 Elementos Muchas aplicaciones tienen pequeños bloques de código de presentación que necesita ser repetido de página en página. CakePHP puede ayudarte a repetir partes de tu website que necesitan ser reutilizadas. Las dos opciones son 'cache' y 'plugin'. situando el renderizado de elementos que se repiten en sus propios ficheros." ?> La función element() combina opciones para el elemento con los datos a pasar en element. Estar partes reutilizadas son llamadas Elementos.10.ctp puede usar la variable $helptext. Anuncios. Los elementos pueden ser usados para hacer una vista más legible.10. Los elementos están en la carpeta /app/views/elements/ y tienen la extensión de archivo . <?php echo $this->element('cajaayuda').3. ?> Dentro del archivo del elemento. controles de navegación. <?php echo $helptext. Son mostrados usando el método element() de la vista. this text is very helpful.ctp. formularios de login y llamadas están a menudo implementadas en CakePHP en forma de elementos. e incluso dentro de otros elementos. Pueden también ayudarte a reutilizar fragmentos de contenido en tus aplicaciones. Un elemento es básicamente una minivista que puede ser incluido en otras vistas. el archivo /app/views/elements/helpbox. Un ejemplo: . cajas de ayuda. array( "helptext" => "Esto es pasado al elemento como $helptext" "foobar" => "Esto es pasado al elemento como $foobar" "cache" => "+2 days" //setea el 'cacheo' a +2 días.<?php echo $this->element('helpbox'. luego haz la llamada requestAction() dentro del segundo parámetro de element() para proveerle al elemento las variables de vista desde tu controlador. ?> Para tener en cache distintas versiones del mismo elemento en una aplicación. } } } ?> Ahora en el elemento podemos acceder el modelo de posts paginados. <?php class PostsController extends AppController { . "plugin" => "" //para renderizar un elemento desde un plugin ) ).'key'=>'unique value') ) ). ?> Puedes aprovechar bien los elementos usando requestAction(). Para tener los últimos cinco posts en una lista ordenada deberíamos hacer lo siguiente: . } else { $this->set(compact('posts')). Crea una acción de controlador que prepare las variables de la vista para tus elementos. para el ejemplo de Post. La función requestAction() trae las variables desde una acción de controlador y las retorna como un array. Para hacer esto. Esto permite que tus elementos cumplan con el diseño MVC. if (isset($this->params['requested'])) { return $posts. provee una clave única de cache usando el siguiente formato: <?php $this->element('helpbox'. array( "cache" => array('time'=> "+7 days".. en tu controlador añade algo como lo siguiente. function index() { $posts = $this->paginate().. asegúrate de ajustar el parámetro 'key' con un nombre diferente cada vez.10. Para llamar a cualquier método de la vista utilice: $this->method() . ?> => array('key' => 'second_use'.3. se usará el elemento del plugin. Si lo ajustas a true. Lee Cache para más información sobre cómo fijar la caducidad.3 Utilizar Elements de un Plugin Si estás usando un plugin y deseas usar elements dentro de ese plugin.2 Cache de Elements Puedes aprovechar el cache de vistas de CakePHP si aportas un parámetro 'cache'. Lo anterior asegura que ambos elementos son almacenados en cache de forma separada. elementos y plantillas. se buscará en la carpeta APP principal. 'var' => $var)). ?> 3. echo $this->element('helpbox'. simplemente especifica el parámetro plugin. ?></li> </ol> <?php endforeach. <?php echo $this->element('helpbox'. Esto evitará que cada sucesiva llamada sobreescriba el resultado almacenado en cache de la anterior llamada element(). De otro modo. mantendrá en cache durante un día. array('cache' => array('key' => 'first_use'.3.10. Si el elemento no existe en el plugin. ?> 3.4 Métodos de la vista Los métodos de la Vista están disponibles para todos los archivos de vistas. Por ejemplo: <?php echo $this->element('helpbox'. array('cache' 'time' => '+1 day'). puedes ajustar tiempos de caducidad alternativos. ?> Si dibujas el mismo elemento más de una vez en una vista y tienes el cache activado. array('plugin' => 'pluginname')). <?php echo $this->element('helpbox'. array('cache' => true)).<h2>Latest Posts</h2> <?php $posts = $this->requestAction('posts/index/sort:created/order:asc/limit:5'). Si la vista está siendo dibujada para un controlador/acción de un plugin. ?> <?php foreach($posts as $post): ?> <ol> <li><?php echo $post['Post']['title']. 'var' => $differentVar)). 3. 'time' => '+1 day').10. 4 error() error(int $code.4. 3. Uses layouts/error.10. Te permite agregar variables al viewVars.5 element() element(string $elementPath. Ver Controller::set() para mas información en el uso de set(). See the section on View Elements for more information and . 'posts'). bool $loadHelpers) Renders an element or view partial. Its important to note that script execution is not stopped by View::error() So you will have to stop code execution yourself if you want to halt the script. 'Not found'.4. 3.4. 3. Devuelve un arreglo con los nombres de las variables.1 set() set(string $var. $this->error(404. sorry'). This will render an error page with the title and messages specified. Usando set() desde tu archivo de vista.4. En tu archivo vista puedes hacer $this->set('activeMenuButton'.10.3.2 getVar() getVar(string $var) Obtiene el valor de la viewVar con el nombre $var 3. 'This page was not found.10.4.ctp to render the page. array $data. mixed $value) Las Vistas tienen un metodo set() que es análogo al set() encontrado en los objetos Controller. string $name.3 getVars() getVars() Obtiene una lista con todas las variables disponibles en el ámbito de la vista actual.10.10. string $message) Displays an error page to the user. agregará las variables a la capa (layout) y elementos (elements) que luego serán renderizados. haciendo sencillo el cambio de la apariencia de tu página de una forma rápida y fácil. mixed $url) Generates a unique non-random DOM ID for an object. } Para declarar qué tema usar por defecto.10. Para usar los temas. class ExampleController extends AppController { var $view = 'Theme'. 'action' => 'index')). necesitas decirle a tu controlador que use la clase ThemeView en lugar de la clase por defecto View. 3.10. This method is most often used from inside helpers. or elements in the layout will not be added to $scripts_for_layout. var $theme = 'ejemplo'. 3. This method is often used by helpers that need to generate unique DOM ID's for elements such as the AjaxHelper. especifica el nombre del tema en tu controlador. //$uuid contains 'form0425fe3bad' 3.7 addScript() addScript(string $name. class ExampleController extends AppController { var $view = 'Theme'. like the Javascript and Html Helpers. array('controller' => 'posts'. based on the object type and url.10.4. This buffer is made available in the layout as $scripts_for_layout. $uuid = $this->uuid('form'. string $content) Adds content to the internal scripts buffer.4.examples. Keep in mind that scripts added from the layout.6 uuid uuid(string $object.5 Temas Puedes aprovechar los temas (themes). This method is helpful when creating helpers that need to add javascript or css directly to the layout. } También puedes setear o cambiar el nombre del tema dentro de una acción o en las funciones de . Dentro de la carpeta themed.ctp. tus hojas de estilo serían almacenadas en /app/webroot/themed/example/css/ y tus archivos JavaScript estarían en /app/webroot/themed/example/js/. puedes almacenarlos en una carpeta de tema dentro de la carpetawebroot/.1 Increasing performance of plugin and theme assets Its a well known fact that serving assets through PHP is guaranteed to be slower than serving those assets without invoking PHP. la estructura de carpetas dentro de /app/views/themed/example/ es exactamente igual a /app/views/. Los archivos de vista de los temas deben estar dentro de la carpeta /app/views/themed/ .js becomes app/webroot/debug_kit/js/my_file. puedes crear archivos de vista maestros y simplemente sobreescribirlos según cada caso dentro de la carpeta de tu tema. se ubicará por defecto en la carpeta webroot principal. De esta forma. $this->theme = 'otro_ejemplo'. Por ejemplo. si un archivo no está en la carpeta del tema.callback beforeFilter() o beforeRender(). Si un archivo de vista no puede ser encontrado en el tema. And while the core team has taken steps to make plugin and theme asset serving as fast as possible. Si tienes archivos CSS o JavaScript que son específicos para tu tema.5.css becomes app/webroot/theme/navy/css/navy. Como con los archivos de vistas.js • app/views/themed/navy/webroot/css/navy. In these situations its recommended that you either symlink or copy out plugin/theme assets to directories in app/webroot with paths matching those used by cakephp. Por ejemplo. CakePHP tratará de localizarlo en la carpeta /app/views/.10. Después de eso. el archivo de vista de una acción de edición de un controlador de Posts estaría ubicado en /app/views/themed/example/posts/edit.css . Los archivos de Layout estarían en /app/views/themed/example/layouts/. there may be situations where more performance is required. crea una carpeta usando el nombre de tu tema. • app/plugins/debug_kit/webroot/js/my_file. 3. Todos los helpers de CakePHP son concientes de los temas y crearán las rutas correctas automáticamente. path mimeType . pudiendo hacer autenticación antes de entregar el archivo al usuario.3. 'download' => true. Después de esto. download Un valor booleano que indica si los encabezados deben forzar la descarga. Por ejemplo. puedes querer tener un directorio de archivos fuera de la carpeta /webroot para prevenir que los usuarios tengan acceso directo a ellos. 'name' => 'ejemplo'. incluyendo su extensión. El nombre del archivo. sólo pasa los parámetros adicionales para especificar dónde está ubicado tu archivo. $this->set($params). incluyendo el separador del directorio final. necesitas decirle a tu controlador que use la clase MediaView en vez de la clase por defecto View. class EjemploController extends AppController { function download () { $this->view = 'Media'. Si el tipo MIME especificado no está en la lista. 'path' => 'files' . Puedes usar vistas Media para traer el archivo desde una carpeta especial dentro de /app/. La ruta (path es relativa a la carpeta app/. } } Parámetros Descripción id El ID es el nombre del archivo tal como está en el servidor. Especifica el nombre sin la extensión del archivo.zip'. 'extension' => 'zip'.10. Un arreglo con tipos MIME adicionales que serán mezclados con la lista interna de tipos MIME aceptados. DS ).6 Vistas de Medios Las vistas de medios te permiten enviar archivos binarios al usuario. Para usar vistas Media. $params = array( 'id' => 'ejemplo. El nombre (name) te permite especificar un nombre de archivo alternativo para ser name enviado al usuario. Esto se compara con una lista interna de tipos MIME extension aceptados. La extensión del archivo. el archivo no será descargado. Cada controlador tiene una propiedad $helpers que lista los helpers que estarán disponibles en la vista. <?php class BakeriesController extends AppController { var $helpers = array('Form'.11. } } ?> 3. no estará disponible.11 Helpers Helpers son las clases simil-componentes para la capa de aplicación de tu aplicación. abarcando las tareas básicas en las que los helpers del núcleo CakePHP pueden ayudarte a lograr. 'Javascript'. agrega el nombre del helper en el arreglo $helpers del controlador. Este capítulo te mostrará cómo crear tus propios helpers. } function mix { // El helper Time no es cargado aquí. Para más información sobre los helpers del núcleo. Digamos que quisieramos crear un helper que pudiera ser usado para mostrar como salida un .3.11. 'Html'. y mantinen al controlador mejor organizado. } ?> También pueden agregar helpers desde dentro de una acción. Para habilitar un helper en tu vista. dirígete a Helpers 'de fábrica'. los helpers son fáciles de crear.1 Usando Helpers Tú usas helpers en CakePHP haciendo que un controlador sepa de su existencia. de esta forma sólo estarán disponibles para esa acción y no para las demás acciones que permanezcan a ese controlador. 3. <?php class BakeriesController extends AppController { function bake { $this->helpers[] = 'Time'. Esto ahorra poder de procesamiento para las demás acciones que no usan el helper. o layouts. Ellos contienen la lógica presentacional que es compartida entre vistas. 'Time').2 Creando Helpers Si un helper del core (o alguno mostrado en Cakeforge o en la Bakery) no se ajusta a tus necesidades. elementos. <?php /* /app/views/helpers/link. $url) { // Lógica para crear un link con un formato específico va aqui. <?php function makeEdit($title.link específicamente creado con CSS que lo necesitas en diferentes partes de tu aplicación... $url) { // Usa la función de salida del helper para enviar // datos con formato de regreso a la vista: return $this->output( "<div class=\"editOuter\"> <a href=\"$url\" class=\"edit\">$title</a> </div>" ). } } ?> Existen algunos métodos incluidos en la clase de Helper en CakePHP de la cual quisieras sacar ventaja: output(string $string) Usa esta función para enviar cualquier información de regreso a tu vista. array('class' => 'edit')). $url) { // Usa el helper de HTML para mostrar // información con formato: $link = $this->Html->link($title. Llamemos a nuestro helper LinkHelper. .1 Including other Helpers Es posible que necesites usar alguna funcionalidad existente en otro helper.php */ class LinkHelper extends AppHelper { function makeEdit($title. function makeEdit($title.2. $url. El archivo de clase se verá algo como esto: <?php /* /app/views/helpers/link.11. } ?> 3.php (usando otros helpers) */ class LinkHelper extends AppHelper { var $helpers = array('Html'). con el formato que usarías en un controlador. Para poder ajustar tu lógica dentro de la estructura existente de helpers de CakePHP. Para hacerlo. puedes especificar los helpers que deseas utilizar con un arreglo $helpers. necesitarás crear una nueva clase en /app/views/helpers. 3.2.3 Usando tu Helper Una vez hayas creado tu helper y de haberlo colocado dentro de /app/views/helpers/.11.php.11. } ?> } 3. Para crear funcionalidad que podría estar disponible para todos los helpers. AppHelper (así como los modelos extienden AppModel y los controladores extienden AppController).2. beforeRender() The beforeRender method is called after the controller's beforeRender method but before the controller's renders views and layout. Una vez tu controller se haya dado cuenta de esta nueva clase. podrás usarla en los views accesando mediante una variable llamada por ese helper: <!-.crear un link usando el nuevo helper --> <?php echo $link->makeEdit('Change this Recipe'. <?php class AppHelper extends Helper { function customMethod () { } } ?> .2 Callback method Helpers feature a callback used by the parent controller class. crea /app/app_helper.3 Creando Funcionalidad para todos los Helpers Todos los helpers extienden a una clase especial. '/recipes/edit/5') ?> Recuerda incluir el FormHelper en el array de $helpers si es apropiado. Los ayudantes Html y Session (si esta activa una sesión) siempre estarán disponibles.11. podrás incluirlo dentro de tus controllers usando la variable especial $helpers.return $this->output("<div class=\"editOuter\">$link</div>"). 3. observadores. Contiene métodos rápidos para drag/drop (levantar/tirar). Detección de proximidad (es este siguiente año?). buen formateo de cadenas de caracteres (Hoy. Helper de CakePHP Ajax Cache Form Html Javascript Number Paginator Rss Session Text Time Xml Descripción Usado en conjunto con Prototype Javascript Library para crear funcionalidad en las vistas. 10:20 am) y conversiones de usos horarios.4 Helpers del Core CakePHP contiene un buen número de helpers que ayudan en la creación de la vista. truncado inteligente de palabras. Usado para 'escapar' valores para usarse en JavaScript. links. Enlaces inteligentes. tablas. ayudan a dar formato a texto. Para más imformación revisa Helpers del Core. Crea formularios HTML y elementos de formulario que se poblan solas y manejan problemas de validación. Ellos asisten en crear notación (markup) con buen formato (incluyendo formas). . Aquí está un resumen de los helpers disponibles por defecto. tags de headers y más. Métodos convenientes para regresar datos RSS feed XML. escribir tus propios objetos JSON. Métodos convenientes para crear código (markup) bien formateado. y también pueden acelerar la funcionalidad de Ajax.3. tiempo y números. Formato para números y tipo de cambio. Es usado por el núcleo (core) para almacenar el contenido de las vistas en caché. Métodos convenientes para crear elementos y headers XML. marcadores. formularios ajax & enlaces. Paginar y ordenar información de modelos. y dar formato a bloques de código. y más. Imágenes.11. Aceso para escribir valores de la sesión en las vistas. el scaffolding ha sido incluido en CakePHP. Una vez que se declare la variable $scaffold en el controlador. es solo una plataforma temporal. Ahora. El scaffolding en CakePHP también permite a los desarrolladores definir cómo los objetos están relacionados unos con otros. que querrás usarla en aplicaciones de producción. forms estandar para editar y vistas estandar para inspeccionar un único elemento en la base de datos.12 Scaffolding El scaffolding (creación de plataformas temporales) en aplicaciones es una técnica que permite a un desarrollador definir y crear aplicaciones básicas que pueden crear. Los esquemas de bases de datos tempranos son sujetos a cambios.. nosotros pensamos que es agradable también. borrar y editar. . en el controlador. Si usted se encuentra realmente tratando de personalizar la lógica en sus vistas. pero por favor. lo cual es perfectamente normal en una parte temprana del proceso de diseño. La consola Bake de CakePHP. Es tan agradable. retirar.. ya estas listo y corriendo. Para reducir el estrés en el desarrollador. actualizar y borrar objetos. Esto tiene su inconveniente: un desarrollador web detesta formas que nunca serán utilizadas.. El scaffolding analiza sus tablas en la base de datos y crea listas estandard con botones de agregar.. es momento de derrumbar su scaffolding para poder escribir más código. y para crear y romper estos enlaces. Todo lo que se necesita para crear un scaffold (plataforma temporal) es un modelo y su controlador. agrega la variable $scaffold: <?php class CategoriesController extends AppController { var $scaffold. es un gran segundo paso: genera todo el código que produciría el mismo resultado que el más reciente escalonado.3. Su estructura flexible la deshaces rápidamente al inicio de un proyecto para poder iniciar. Te permite tener una aplicación CRUD básica funcional en minutos. } ?> . fue pensada como una manera temporal de iniciar un proyecto rápidamente. Scaffolding es una gran manera de iniciar las primeras partes de una aplicación web iniciada. Para agregar scaffolding a tu aplicación. El scaffolding de CakePHP es bastante agradable. vista en la siguiente sección. No fue pensada para ser completamente flexible. ese cuenta lo que el scaffoldding (plataforma temporal) es. bueno. scaffolding to generate an admin interface. var $displayField = 'first_name'. así que si tu modelo Category pertenece a un User ["belongsTo" User]. lo logrará activando la variable $displayField en el modelo. var $scaffold = 'admin'. usted verá las IDs del modelo User relacionadas en el listado del modelo Category. ya estás listo para comenzar!.com/admin/controller/add http://example. <?php class User extends AppModel { var $name = 'User'.php.prefixes'. array('admin')). Once you have enabled admin routing assign your admin prefix to the scaffolding variable.com/admin/controller/index http://example. Por ejemplo. Inicializaremos la variable $displayField en nuestra clase de User para que los usuarios relacionados con categorías serán mostrados por el primer nombre en lugar de solo el ID en el scaffolding. You will now be able to access admin scaffolded actions: Plain Text View http://example.com/categories para ver tu nuevo scaffold.com/admin/controller/view http://example.Si has creado la clase basica del modelo Category en el archivo /app/models/category.php.com/admin/controller/edit http://example. El crear métodos en controladores que se hayan sido "scaffoldeados" pueden causar resultados no deseados. Esta característica hace el scaffolding más leíble en muchas instancias. su método index será mostrado en lugar que la funcionalidad de scaffoling. } ?> 3. Si usted quisiera ver algo aparte de un ID (como el apellido del usuario). Visita http://example.1 Creating a simple admin interface with scaffolding If you have enabled admin routing in your app/config/core. you can with use Configure::write('Routing. El scaffolding es reconocido por sus asociaciones de modelos. si crea un método index() en un controlador "scaffoldeado".com/admin/controller/delete .12. La personalización se hace creando templetes de vistas: Vistas personalizadas para un controlador específico (para este ejemplo PostsController) debe ser colocado como lo siguiente: /app/views/posts/scaffold.ctp Las vistas scaffolding personalizadas para todos los controladores deberían ser colocadas como las siguientes: /app/views/scaffolds/index.ctp /app/views/scaffolds/new.ctp /app/views/scaffolds/edit. puees crear templetes.ctp /app/views/posts/scaffold.2 Personalizando Vistas Scaffold Si estás buscando para algo un poco diferente a tus vistas scaffolded. As with normal scaffolding you can override individual methods and replace them with your own.12.new.show.index.ctp /app/views/posts/scaffold.edit. pero semejante personalización puede ser muy útil durante el esarrollo e prototipos.ctp /app/views/posts/scaffold. Keep in mind that you cannot have both admin and non-admin methods scaffolded at the same time.ctp /app/views/scaffolds/add.This is an easy way to create a simple backend interface quickly.ctp /app/views/scaffolds/show.ctp . 3. function admin_view($id = null) { //custom code here } Once you have replaced a scaffolded action you will need to create a view file for the action as well. Nosotros aún no recomendamos esta técnica para aplicaciones de producción. Este ejemplo asume que el usuario ya esta logeado en una sesión de bash y se encuentra en la raíz de la instalación de CakePHP. hay que asegurarnos de que podemos correr CakePHP en consola. Welcome to CakePHP v1.2 Console --------------------------------------------------------------Current Paths: -working: /path/to/cake/ -root: /path/to/cake/ -app: /path/to/cake/app/ -core: /path/to/cake/ Changing Paths: your working path should be the same as your application path . y para regresar sus parámetros. Primero. esta sección es para ti. Los ejemplos mostrados en esta sección serán en bash. La Consola CakePHP provee un framework para crear scritps de shell. Antes de que nos metamos en cosas más específicas./cake/console/cake Pero el uso preferido es agregando el directorio de consola a tu ruta para que puedas usar el comando cake en cualquier lado: $ cake Ejecutar la Consola sin argumentos produce el siguiente mensaje de ayuda: Hello user. pero la Consola e CakePHP es compatible con Windows también. Puedes técnicamente correr la consola usando algo similar a esto: $ cd /my/cake/app_folder $ . necesitas abrir una terminal el sistema. Una versión de línea de comandos (cli) de PHP debe de estar disponible en el sistema si planeas usar la Consola..3. PHP provee un poderoso cliente CLI que hace que crear interfases entre tus archivos de sistema y tus aplicaciones sea mucho más sencillo.13 La consola de CakePHP Esta sección provee una introducción a CakePHP en la línea de comandos. La Consola usa una configuración del tipo despachador para cargar una un shell o una tarea. Ejecutemos el programa de Consola desde bash. Si tu alguna vez has utilizado tus clases MVC de CakePHP en un cron job o cualquier otro script de línea de comandos. Para cambiar la carpeta de app con el que deseas trabajar. Para este ejemplo. Imprimiendo las rutas del directorio de trabajo root.1. Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp Available Shells: app/vendors/shells/: . Muchos usuarios agregan la Consola de CakePHP a sus rutas del sistema operativo para que pueda ser ejecutado fácilmente. asumiendo que ya has agregado el carpeta de la consola en tu PATH: $ cake -app /path/to/app La ruta provista puede ser relativa al directorio de trabajo actual o provista como una ruta absoluta. type 'cake shell_name [args]' To get help on a specific command.to change your path use the '-app' param. Primero. crearemos una ‘report' shell que imprima datos de un modelo. Este siguiente ejemplo nos muestra como especificar un folder de app. app. Esto es epecialmente útil si estas ejecutando la consola de diferentes partes del sistema de archivos.1 Creando Shells y Tasks 3. 3.13. y core te permite ver donde la Consola hará los cambios. type 'cake shell_name help' La primera información mostrada se relaciona con las rutas. crea un report.1 Creando tus Propios Shells Crearemos una shell para usarla en la Consola.none cake/console/libs/: acl api bake console extract To run a command. <?php class ReportShell extends Shell { function main() {} } ?> .php en /vendors/shells/. puedes proporcionar su ruta como el primer argumento al comando cake.none vendors/shells/: .13. "\n"). //Imprime el total de las ordenes seleccionadas. podemos usarlo en el método main(). } number_format($order['Order'] $this->out('----------------------------------------' . En este ejemplo. foreach($orders as $order) { $this->out('Order date: ' . function main() { } } ?> Una vez que agregamos nuestro modelo al arreglo $uses. strtotime('-1 month')). Esto deberá regresar lo siguiente: . function main() { //Agrega las órdenes enviadas el mes pasado. $orders = $this->Order>find("all". $this->out("Total: $" . Agreguemos algunos modelos al shell para que podamos crear un reporte de algún tipo.shipped >= '$month_ago'")). //Imprime la información de cada orden. 2) . pero no hará mucho. $total += $order['Order']['amount']. 2) . ya podemos correr el shell. $order['Order']['created'] ['amount']. Esto se hace de la misma forma que en el controlador: al agregar los nombres de los modelos a la variable $uses. number_format($total. $month_ago = date('Y-m-d H:i:s'. "\n"). } } Debes de ser capás de correr este reporte ejecutando este comando (si el comando cake esta en tu PATH): $ cake report donde report es el nombre del archivo shell en /vendor/shells/ sin la extensión . <?php class ReportShell extends Shell { var $uses = array('Order'). "\n"). nuestro modelo Order debe de ahora ser accesible como $this->Order en el método main() de nuestro nuevo shell.php.Desde este punto. $this->out('Amount: $' . . "\n").array('conditions'=>"Order. Aquí hay un ejemplo simple de la lógica que podemos usar en este shell: class ReportShell extends Shell { var $uses = array('Order'). Por ejemplo en la shell core de bake.78 2007-07-30 21:16:03 $83. . } ?> Los tasks son almacenados en /vendors/shells/tasks/ en archivos con el nombre de sus clases. 'DbConfig'. 'Model'. Así que si quisiéramos crear una nueva tarea ‘cool’ .56 $867.52 2007-07-29 01:40:52 $183.26 2007-07-29 01:42:22 $134.2 Tasks (Tareas) Los tasks (tareas) son pequeñas extensiones para los shells. y son agregadas a nuestros shells usando una variable de clase especial $task. 'View'.13.1. la clase CookTask (que extiende de Shell) sería colocada en /vendors/shells/tasks/cool. La clase VeryCoolTask (que extiende Shell) sería colocada en /vendors/shells/tasks/cool.75 --------------------------------------------------------------Order date: Amount: Order date: Amount: Order date: Amount: Order date: Amount: Order date: Amount: Total: CakePHP v1. hay un cierto número de tasks definidos: <?php class BakeShell extends Shell { var $tasks = array('Project'.php.63 2007-07-29 15:52:42 $423.Hello user. 'Controller'). Nos permiten que haya lógica comapartida entre shells.php. Welcome to App : app Path: /path/to/cake/app 2007-07-30 10:31:12 $42.2 Console --------------------------------------------------------------- ---------------------------------------- ---------------------------------------- ---------------------------------------- ---------------------------------------- ---------------------------------------- 3. Puedes también acceder a los tasks directamente desde la línea de comados: $ cake sea sound 3. it will be unavailable to the cronjob. do if [ "$1" = "-cli" ] || [ "$1" = "-console" ]. (chmod +x cakeshell) #!/bin/bash TERM=dumb export TERM cmd="cake" while [ $# -ne 0 ]. However. //Se encuentra en /vendors/shells/tasks/sound.php { var $tasks = array('Sound').php function main() { $this->Sound->execute(). Copy and save this to your vendors folder as 'cakeshell' and don't forget to make it executable. <?php class SoundTask extends Shell { var $uses = array('Model'). when you have added the console path to the PATH variable via ~/.profile.los shells llamarán a este método para iniciar la lógica del task. then PATH=$PATH:$2 . The following BASH script will call your shell and append the needed paths to $PATH.13. // Lo mismo que en el controlador var $uses function execute() {} } ?> Puedes acceder a tasks dentro de tus clases de shell y ejecutarlas desde ahí: <?php class SeaShell extends Shell // Se encuentra en /vendors/shells/sea. } } ?> Un método llamado “sound” en la clase SeaShell podría sustituir la habilidad de acceder a la funcionalidad de la tarea Sound especificada en el arreglo $tasks.2 Running Shells as cronjobs A common thing to do with a shell is making it run as a cronjob to clean up the database once in a while or send newsletters.Cada task debe de tener por lo menos un método execute() . x/cake/console -app /full/path/to/app A simple trick to debug a crontab is to set it up to dump it's output to a logfile./vendors/cakeshell myshell myparam -cli /usr/bin -console /cakes/1. As a cronjob this would look like: # m h dom mon dow command */5 * * * * /full/path/to/cakeshell myshell myparam -cli /usr/bin -console /cakes/1. You can do this like: # m h dom mon dow command */5 * * * * /full/path/to/cakeshell myshell myparam -cli /usr/bin -console /cakes/1. el plugin opera en su propio espacio.x. . plugin.x/cake/console The -cli parameter takes a path which points to the php cli executable and the -console parameter takes a path which points to the CakePHP console. comportándose como si fuese una aplicación en sí mismo.2.log 3.shift else cmd="${cmd} $1" fi shift done $cmd You can call it like: $ . un blog sencillo o un web service en alguna de tus aplicaciones? Empaquétalo como un plugin de CakePHP e inclúyelo en otras aplicaciones.2.x. ¿Has desarrolado un módulo de administración de usuarios amigable.x. modelos y vistas y publicarlos como una aplicación empaquetada.14 Plugins CakePHP nos permite combinar una serie de controladores. etc.).2.x/cake/console -app /full/path/to/app >> /path/to/log/file. que otros pueden integrar en sus aplicaciones CakePHP. Por lo demás. El principal vínculo entre un plugin y la aplicación en la que se instala es la configuración de la aplicación (conexión a la base de datos. El nombre del directorio padre del plugin es importante y se referenciará muy a menudo.. 3.3.14. necesitaremos el controlador PeticionesController para este plugin.clase AppModel del plugin Si queremos acceder a nuestro plugin a través de una URL. con el fin de evitar conflictos de espacios de nombre con la aplicación principal..modelos del plugin <. } ?> Si olvidamos definir estas clases.controladores del plugin <.php: <?php class PizzaAppController extends AppController { //. Puesto que las acciones que más trataremos son las peticiones de pizza.php /pizza_app_model.php: <?php class PizzaAppModel extends AppModel { //. conviene llamar los controladores de los plugin con un nombre relativamente único. Para empezar tendremos que colocar todos los archivos de nuestro plugin en el directorio /app/plugins.vistas del plugin <.php <. Para este ejemplo utilizaremos el nombre 'pizza'. Aunque no es necesario. En nuestro ejemplo: // /app/plugins/pizza/pizza_app_controller.. CakePHP nos mostrará el error "Missing Controller". La estructura de archivos será la siguiente: /app /plugins /pizza /controllers /models /views /pizza_app_controller. vamos a crear un nuevo plugin que encargue pizzas.1 Crear un Plugin Como ejemplo.2 Controladores del Plugin Los controladores de nuestro plugin pizza se almacenan en /app/plugins/pizza/controllers/. así que conviene escogerlo con prudencia. Estas dos clases especiales tienen el nombre del plugin y extienden las clases AppController y AppModel de la aplicación principal. } ?> // /app/plugins/pizza/pizza_app_model.. es necesario definir las clases AppController y AppModel para el mismo.clase AppController del plugin <.14. . 14. En el apartado anterior. quedando: // /app/plugins/pizza/controllers/pizza_peticiones_controller. en /app/plugins/pizza/controllers. var $uses = array('Pizza. anteponiendo a todas las clases de nuestro plugin el nombre del mismo.. ubicamos nuestro nuevo controlador. o anteponer el nombre del plugin al nombre de la clase (PizzaPeticionesController en nuestro ejemplo). Deberías obtener un error “Missing Model”. pero no es necesaria en este caso. al nombre del modelo se añade como prefijo el nombre del plugin. definición consistente con el esquema de nombres que establecimos previamente.php: class PizzaPeticion extends PizzaAppModel { var $name = 'PizzaPeticion'. visita /pizza/pizzaPeticiones. Si necesitamos referenciar un modelo dentro de nuestro plugin.php class PizzaPeticionesController extends PizzaAppController { var $name = 'PizzaPeticiones'.. Esta línea de código se añade por claridad. Este será el próximo paso. PeticionesController o ProductosController: deberíamos ser creativos con los nombres de los controladores.No es extraño pensar que una aplicación podría tener controladores como UsuariosController. Así. tenemos que incluir el nombre . Ahora crearemos el modelo para ese controlador. PizzaPeticion.3 Modelos del Plugin Los modelos de un plugin son almacenados en /app/plugins/pizza/models. // /app/plugins/pizza/models/pizza_peticion.PizzaPeticion'). function index() { //. } } Observa cómo este controlador extiende el controlador AppController del plugin (llamado PizzaAppController) en lugar del controlador AppController de la aplicación principal. 3. Además. PizzaPeticionesController. definimos el controlador PizzaPeticionesController para nuestro plugin de ejemplo. Pizza. } ?> Acceder a /pizza/pizzaPeticiones ahora (suponiendo que tenemos una tabla en nuestra base de datos llamada ‘pizza_peticiones’) nos debería dar un error “Missing View”. Si quieres acceder a lo que hemos hecho hasta ahora. porque no hemos definido todavía el modelo PizzaPeticion. Ayudantes (Helpers) y Comportamientos (Behaviors).Ejemplo'). Hacer referencia a nuestros componentes desde el mismo plugin no requiere ninguna notación especial. un plugin puede tener Componentes (Components). Incluso podemos crear plugins que incluyan únicamente estas clases. Para invocar el Componente desde fuera del entorno del plugin. separados por un punto. var $components = array('Pizza.del plugin junto con el nombre del modelo. necesitaremos una vista para la acción PizzaPeticionesController::index(): // /app/plugins/pizza/views/pizza_peticiones/index. var $components = array('PluginNombre.4 Vistas del Plugin Las vistas se comportan en un plugin exactamente igual a como lo hacen en una aplicación normal.14.--> 3. // referencia al componente . } ?> 3.. Para nuestro plugin encargado de pedir pizza.14.5 Componentes. Basta con colocarlas en la carpeta adecuada en /app/plugins/[plugin]/views/.Ejemplo'). sin ninguna convención de nombres especial. // Componente class EjemploComponent extends Object { } // desde los controladores de nuestro plugin: var $components = array('Ejemplo'). Los componentes se desarrollan exactamente de la misma manera a como se desarrollan en una aplicación normal. Ayudantes y Comportamientos Al igual que una aplicación típica.Aquí debería ir un formulario para solicitar pizza.PizzaPeticion'). var $hasMany = array('Pizza.ctp: <h1>Pide una Pizza</h1> <p>¡Nada combina con Cake mejor que una buena pizza!</p> <!-. tenemos que indicar el nombre del mismo. siendo un mecanismo excelente para construir módulos reutilizables que pueden añadirse fácilmente en cualquier proyecto.php: class ExampleModel extends PizzaAppModel { var $name = 'ExampleModel'.. // /app/plugins/pizza/models/pizza_peticion. a un plugin llamado 'usuarios' con un controlador UsuariosController podemos acceder en /usuarios/add si no hay ningún plugin llamado AddController en la carpeta [plugin]/controllers. podemos acceder al mismo siguiendo la URL /nombreplugin/nombrecontrolador/accion. . Las líneas anteriores son ejemplos de como incluir archivos javascript y css en nuestro plugin. accedemos a PizzaPeticionesController en /pizza/pizzaPeticiones.6 CSS y Javascript en los Plugins Podemos incluir archivos CSS y Javascript en nuestros plugins. • Podemos definir el diseño de nuestros plugins en app/plugin/views/layouts. La misma técnica se aplica a los Ayudantes y Comportamientos. Por ejemplo. Después de instalar el plugin en /app/plugins. en nuestro_plugin/vendors/css y nuestro_plugin/vendors/js. 3. ?> <?php echo $javascript->link('/nuestro_plugin/js/mi_javascript'). respectivamente. los diseños en /app/views/layouts folder by default. <?php echo $html->css('/nuestro_plugin/css/mi_css'). Algunos consejos útiles a tener en cuenta cuando trabajamos con plugins en nuestras aplicaciones CakePHP: • Si no definimos [Plugin]AppController y [Plugin]AppModel. nuestro plugin está listo para ser distribuido (aunque sería conveniente añadir unos cuantos extras. Es importante observar como se incluye en la ruta del arhivo js o css /nuestro_plugin/. por defecto.14. los plugins usarán. En nuestro plugin de ejemplo para ordenar pizza. colocándolos. Esto hace que la magia funcione 3.14. • Podemos tener un controlador por defecto con el mismo nombre de nuestro plugin. recibiremos errores "Missing Controller" cuando intentemos accede a un controlador del plugin.EjemploComponent en el plugin Pizza. En caso contrario. Podemos acceder a él via /[plugin]/accion. como un readme o un fichero SQL). Pueden incluirse en nuestras vistas usando los ayudantes nativos del framework.7 Consejos para desarrollar Plugins Una vez que ya hemos desarrollado todo lo necesario. boolean $return = false) Esta función gestiona la localización en las aplicaciones CakePHP.. en los controladores logramos comunicar distintos plugins. Muchas de ellas están orientadas al trabajo con las clases de CakePHP (cargando las clases de modelo o componente). 3. además de ser útiles a la hora de referenciar ciertos archivos o directorios en nuestras aplicaciones CakePHP. mientras que el segundo parámetro indica si se debe mostrar automáticamente la cadena (por defecto). Lo más probable es que si estás buscando una función para realizar una tarea con mucha frecuencia. es útil conocer una serie de funciones globales que ofrece CakePHP. o devolverla para su procesamiento (pasar el valor true para que esto suceda). pueden surgir errores PHP del tipo "clase redefinida .15 Constantes y Funciones Globales Aunque en la mayor parte de nuestro trabajo diario en CakePHP utilizaremos clases y métodos nativos. Usarlas ayuda a realizar actualizaciones menos complicadas. pero muchas otras hacen más sencillo trabajar con matrices o cadenas. Si no fuera así.1. También veremos en esta sección algunas de las constantes disponibles en las aplicaciones CakePHP.15.1 __ __(string $string_id. El parámetro $string_id identifica la ID de una traducción.15.• Usando $this->requestAction('/plugin/controller/accion'). pero otras (como vendor() y uses()) se pueden usar para incluir código o realizar otras funciones útiles. . debemos asegurarnos de que los nombres de modelos y controladores son tan únicos como sea posible.1 Funciones globales Éstas son las funciones globales disponibles en CakePHP.. Visita la sección Localización e Internacionalización para más información. la encuentres aquí." 3. • Si pretendemos usar requestAction. Muchas de ellas simplemente facilitan la llamada a funciones de PHP con nombres largos. 3. 4 am am(array $uno. $dos.1.1. // salida: array( 'a' => 'b' ) 3.) Combina todos los arrays pasados como parámetros y devuelve el array resultante. [1] => 'bar' ) 3.15. La función checa si existe el archivo antes de incluir y regresa un booleano. $dos. $dos.15.) Devuelve un array con los parámetros pasados a la función. $tres. Toma un número opcional de argumento. $tres. 3..15.2 a a(mixed $uno.1.15. Ejemplo: config('some_file'.5 config Puede ser usado para cargar archivos desde el directorio config mediante include_once.3. // salida: array( [0] => 'foo'. $tres..) Crea arrays asociativos a partir de los parámetros enviados a la función. print_r(a('foo'..1.3 aa aa(array $uno.. echo aa('a'.'b').. 'bar')).. 'myconfig'). . 1. Devuelve la cadena convertida. Alternativa si las variables $_SERVER o $_ENV están deshabilitadas.1. es distinto de cero. Devuelve un valor booleano. También permite emular las variables PHP_SELF y DOCUMENT_ROOT en los servidores que no permitan su uso.1.15.1.15. ya que ofrece la misma funcionalidad y es totalmente compatible.15.10 fileExistsInPath fileExistsInPath(string $archivo) Comprueba que el fichero $archivo está en el include_path actual de PHP. 3.8 e e(mixed $datos) Simplifica la llamada a la función echo().15. . 3.15. 3. boolean $showHtml = false) Si el nivel de depuración. De hecho.1.3. los datos se formatean para mostrarlos adecuadamente en los navegadores web.9 env env(string $key) Obtiene una variable de entorno a partir de las fuentes disponibles.7 debug debug(mixed $var. Si $showHTML es true. es una buena práctica usar env() en lugar de $_SERVER o getenv() (sobretodo si pensamos distribuir el código). variable de configuración DEBUG.6 convertSlash convertSlash(string $cadena) Sustituye las barras ("/") por subrayados ("_") y elimina el primer y el último subrayados en una cadena. 3. se muestra $var. 17 up up(string $cadena) Alias de la función strtoupper().12 ife ife($condicion. string $charset) Alias de la función htmlspecialchars(). 3. devuelve $siNoVacia.15.15. 3.3. si no. string $cadena_sustituta. 3.15.15.1.1. string $cadena_original) Alias de la función str_replace(). devuelve $siVacia.1. .1.16 stripslashes_deep stripslashes_deep(array $valor) Elimina recursivamente las barras invertidas de $valor. Devuelve el array modificado.15.15.15. 3. añadiendo la etiqueta <pre> a la salida.1.11 h h(string $texto.1. 3. Si $condicion no es vacía.14 pr pr(mixed $var) Alias de la función print_r(). $siNoVacia.1. $siVacia) Útil en operaciones ternarias.15 r r(string $cadena_buscada. 3.13 low low(string $cadena) Alias de la función strtolower(). 15. $lib2. directorio helpers. directorio cake.php'.. directorio de archivos de cache. directorio layouts. $lib3. directorio CakePHP Library de los tests. directorio app. JS LAYOUTS LIB_TESTS LIBS directorio de archivos JavaScript (en webroot).1.. directorio raíz.18 uses uses(string $lib1. CONTROLLER_TES directorio controller de los tests.15. Pasar como parámetro el nombre de la librería sin la extensión '. directorio de archivos CSS. TS CONTROLLERS CSS ELEMENTS HELPER_TESTS HELPERS INFLECTIONS directorio controllers. 3.. directorio de archivos de configuración. directorio elements.2 Constantes predefinidas constante APP APP_PATH CACHE CAKE COMPONENTS CONFIGS Ruta absoluta dentro de la aplicación al . directorio helper de los tests. directorio de librerías CakePHP. directorio components.3. directorio inflections (normalmente dentro del directorio de configuración). .) Permite cargar las librerías nativas de CakePHP (localizadas en cake/libs/).. directorio models. directorio model de los tests.3. directorio tests (directorio padre de los directorios de test para los modelos. ruta completa a webroot.1 Vendor assets Support for vendor assets have been removed for 1.) TMP VENDORS VIEWS WWW_ROOT directorio tmp. etc. 3.16 Paquetes de terceros (Vendor packages) 3. .LOGS MODEL_TESTS MODELS SCRIPTS TESTS directorio de logs (en app). directorio views.16. controladores. directorio vendors. It is recommended that you take any vendor assets you have and repackage them into plugins. See Plugin assets for more information. directorio de scripts de Cake. el ejemplo de abajo muestra algunas simples reglas de validación que se aplican a esos campos: <?php class User extends AppModel { var $name = 'User'. Hay muchos diferentes aspectos del proceso de validación. tu podrias querer que los passwords tengan a lo menos un largo de ocho caracteres. o asegurar que los username sean únicos. por ejemplo: <?php class User extends AppModel { var $name = 'User'. Asumiendo que la tabla users tiene los campos login. el arreglo $validate se agrega al modelo User. lo que ocurre cuando tu llamas al método save() de tu modelo. } ?> . email y born. 'email' => 'email'. ya que asegura que los datos en un modelo están conformes a las reglas de negocio de la aplicación. revisa la sección que cubre el FormHelper.1 Validación de Datos La validación de los datos es una parte importante de cualquier aplicación. pero el arreglo no contiene reglas de validación. var $validate = array( 'login' => 'alphaNumeric'. En esta sección cubriremos el lado del modelo. es decir. password. El primer paso en la validación de datos es la creación de las reglas de validación en el Modelo. usa el arreglo Model::validate en la definición del Modelo. var $validate = array(). 'born' => 'date' ). Para más información acerca de cómo manejar el despliegue de errores de validación. } ?> En el ejemplo de arriba. Para hacer eso.4 Tareas comunes con CakePHP 4. Definir reglas de validación hace que el manejo de los formularios sea muchísimo más fácil. Por ejemplo. 'email' => 'email'. 'password' => array( 'rule' => array('minLength'. La definición de reglas de validación habilitan en CakePHP el despliegue automático de mensajes de error en formularos si los datos enviados no cumplen las reglas de validación. y números de tarjeta de crédito las cubriremos en detalle más adelante. var $validate = array( 'login' => array( 'alphaNumeric' => array( 'rule' => 'alphaNumeric'. 5. URLs. Algunas de las reglas incluidas permiten verificar el formato de los emails. 'born' => array( 'rule' => 'date'. Acá tenemos un ejemplo de validación más complejo que aprovecha algunas de las reglas incluidas: <?php class User extends AppModel { var $name = 'User'. 'between' => array( 'rule' => array('between'. 'message' => 'Sólo letras y números' ). CakePHP incluye muchas reglas de validación y usarlas puede ser bastante simple. 'message' => 'Ingrese una fecha válida'. y born debe ser una fecha válida. 'message' => 'Largo mínimo de 8 caracteres' ). 'message' => 'Entre 5 y 15 caracteres' ) ). } ?> . 15).El ejemplo muestra cómo se pueden agregar reglas de validación a los campos de un modelo. '8'). 'allowEmpty' => true ) ). el email debe ser válido. Para el campo login serán aceptadas sólo letras y números. 'required' => true. Ahora que viste a grandes rasgos cómo funciona la validación. 'on' => 'create'. un único campo puede tener múltiples reglas de validación. Donde.1. y born debe ser una fecha válida.. Hay tres diferentes maneras para definir reglas de validación: arreglos simples. 4. 'fieldName' es el nombre del campo para el cual se está definiendo una regla. esta es la manera más simple de definir una regla de validación. El email debe contener una dirección de correo válida. // ó: array('ruleName'. veamos el patrón de uso general para agregar una regla a un solo campo: var $validate = array( 'fieldName1' => array( 'rule' => 'ruleName'. Como lo muestra el ejemplo de arriba. Y si las reglas incluidas no coinciden con lo que necesitas. // ó: 'update' 'message' => 'Su mensaje de error' ) ). y 'ruleName' es el nombre de una regla pre-definida (cubriremos esto en la siguiente sección). 'param2' . La sintaxis para la definición de reglas usando esta manera es: var $validate = array('fieldName' => 'ruleName'). 4. 'param1'.. una única regla por campo.Dos reglas de validación son definidas para login: debería contener sólo letras y números. veamos cómo estas reglas son definidas en el modelo.1. . y múltiples reglas por campo. notar que puedes agregar mensajes de error propios que CakePHP mostrará cuando estas reglas de validación no se cumplan. 'allowEmpty' => false. puedes agregar tus propias reglas de validación según tus requerimientos.) 'required' => true.1 Reglas Simples Tal como el nombre lo sugiere. Además. Pero antes de su discusión. y su largo debe ser de 5 a 15.2 Una regla por campo Ésta técnica de definición permite un mejor control del funcionamiento de las reglas de validación. El campo password debe tener un largo mínimo de 8 caracteres. 1 rule El índice ‘rule’ define el método de validación y acepta un sólo valor o un arreglo. Si la regla requiere algunos parámetros (como max.2 required Este índice debería tener asignado un valor booleano. ‘required’. un método de la clase core Validation. Para un completo listado de todas las reglas incorporadas ver la sección llamada "Reglas de Validación Incorporadas". el campo debe estar presente en el arreglo de datos. Si ‘required’ es true. Si sólo se setea 'required' => true la validación del formulario no funcionará correctamente. El valor para ‘rule’ especificado puede ser el nombre de un método en tu modelo. 8) ) ). ‘rule’ puede ser un sólo valor. cada campo (arriba se está mostrando sólo un campo) es asociado con un arreglo que contiene cinco índice: ‘rule’. 4. Si la regla no requiere parámetros. el índice ‘rule’ es requerido para la definición de reglas basadas en arreglos. 4.1. min o range).2. Recuerda. ‘allowEmpty’.2. ‘on’ y ‘message’. Veamos con más detalle cada uno de estos índices. Esto debido a que 'required' no es en realidad una regla.El índice 'rule' es requerido. o una expresión regular. Por ejemplo. entonces ‘rule’ debería ser un arreglo: var $validate = array( 'password' => array( 'rule' => array('minLength'. por ejemplo: var $validate = array( 'login' => array( 'rule' => 'alphaNumeric' ) ). si la regla de validación ha sido definida de la siguiente manera: . Como puedes ver.1. Si el índice login están presente pero no tiene un valor asignado. Esto provee un mecanismo que permite que cierta regla sea aplicada ya sea durante la creación de un nuevo registro. entonces la regla sólo será verificada durante la creación de un nuevo registro. Setear ‘required’ a true sólo verifica que el índice del campo este presente. o durante la actualización de un registro. El valor por defecto para este índice es un false booleano. Si no es así. la regla sólo será verificada durante la actualización de un registro. la validación falla (la regla no se cumple). 4. De igual manera.3 allowEmpty Al índice allowEmpty se le debería asignar un valor booleano. 'required' => true ) ). Cuando se deja en true.var $validate = array( 'login' => array( 'rule' => 'alphaNumeric'. Si allowEmpty es false.1.1.4 on El índice ‘on’ puede ser seteado con uno de los siguientes valores: ‘update’ o ‘create’. un campo vacío pasará exitosamente la validación de ese campo.2. Esto significa que el campo siempre procesará las reglas de validación incluyendo la ejecución de funciones de validación propias. la regla será verificada durante la creación y actualización de un registro.2. Si la regla fue definida como ‘on’ => ‘create’. la validación será exitosa. Cuando ‘on’ es null. los datos pasados al método save() del modelo deben incluir el campo a un valor no-vacío para ese campo. El valor por defecto de ‘on’ es null. El valor por defecto de allowEmpty es null. 4.5 message El índice ‘message’ permite definir un mensaje de error de validación para la regla: .2.1. Los datos enviados al método save() del modelo deben contener un valor para el campo login. si es definida como ‘on’ => ‘update’. 4. ). // acá van índices extra como on. esto es bastante similar a lo que hicimos en la sección previa. La siguiente técnica que revisaremos nos permite asignar múltiples reglas de validación por cada campo de un modelo. Anteriormente. En este caso.3 Múltiples Reglas por Campo La técnica descrita anteriormente nos entrega mayor flexibilidad que la asignación de reglas simples. Como puedes ver. 'message' => 'Password debe tener a lo menos 8 caracteres' ) ). pero hay un paso adicional que podemos tomar para lograr un control más fino de la validación de datos. Si quieres asignar múltiples reglas de validación a un sólo campo. 8). etc. por cada campo teníamos un sólo arreglo con parámetros de validación. // acá van índices extra como on. Cada ‘nombreRegla’ contiene un arreglo distinto con parámetros de validación. required.var $validate = array( 'password' => array( 'rule' => array('minLength'. 4. básicamente así es cómo se verá: var $validate = array( 'nombreCampo' => array( 'nombreRegla' => array( 'rule' => 'nombreRegla'. cada ‘nombreCampo’ consiste en un arreglo de índices de reglas. ) ) ). required.1. 'nombreRegla2' => array( 'rule' => 'nombreRegla2'. Esto se entiende mejor con un ejemplo práctico: . etc. los nombres de índice son similares a las reglas que usan. y puedes eliminar los mensajes del modelo. 'message' => 'Se permiten sólo letras y números'. pero el nombre de índice puede ser cualquier nombre. Para más información acerca de la función __() ver "Localization & Internationalization" . entonces se mostrará el mensaje de error para esa regla y no se validará ninguna regla adicional. Por defecto CakePHP trata de validar un campo usando todas las reglas de validación declaradas para él y retorna un mensaje de error para la última regla no satisfecha. 'message' => 'Largo mínimo de 8 caracteres' ). 'minlength' => __('Largo mínimo de 8 caracteres'. array( 'label' => __('Login'.var $validate = array( 'login' => array( 'alphanumeric' => array( 'rule' => 'alphaNumeric'. El ejemplo de arriba define dos reglas para el campo login: alphanumeric y minLength. cada regla se identifica con un nombre de índice. Si vas a usar mensajes de error internacionalizados podrias quierer especificar los mensajes de error en las vistas: echo $form->input('login'. true) ) ) ). true). 'last' => true ). true). Asi que si prefieres mostrar un mensaje de error para la primera regla no satisfecha entonces debes dejar 'last' => true por cada regla. Como puedes ver. '8'). Pero si el índice last es dejado como true y la regla no es satisfecha. En este caso particular. 'minlength' => array( 'rule' => array('minLength'. El campo ahora está totalmente internacionalizado. ) ). 'error' => array( 'alphanumeric' => __('Se permiten sólo letras y números'. 'message' => 'Los nombres de usuario deben contener sólo letras y números. 4.4. 15). 4.' ) ). Abajo encontrarás una lista completa de todas las reglas. 5.3 blank Esta regla es usada para asegurar que el campo es dejado en blanco o con sólo espacios en blanco como su valor.1.1.1 alphaNumeric Los datos para el campo deben contener sólo letras y números.1.4. Se debe indicar un valor mínimo y máximo. retorno de carro y nueva línea. junto ejemplos de uso. 'message' => 'Las contraseñas deben tener un largo entre 5 y 15 caracteres.4. 4. Esta clase contiene muchas técnicas de validación frecuentemente usadas que no necesitarás escribir por tu cuenta.4 Reglas de Validación Incorporadas La clase Validation de CakePHP contiene muchas reglas de validación incorporadas que pueden hacer mucho más fácil la validación de datos.2 between El largo de los datos para el campo debe estar dentro de un rango numérico específico. . var $validate = array( 'login' => array( 'rule' => 'alphaNumeric'.' ) ). Los espacios en blanco incluyen los caracteres de la barra espaciadora. var $validate = array( 'password' => array( 'rule' => array('between'.4. tabulador.1. ‘all’ o cualquiera de los siguientes: • bankcard • diners • disc • electron • enroute • jcb • maestro • mc • solo • switch • visa .1. Los valores aceptados son “true” o “false”.var $validate = array( 'id' => array( 'rule' => 'blank'.5 cc Esta regla es usada para verificar si los datos corresponden a un número de tarjeta de credito válido.4 boolean El campo debe contener un valor booleano. ‘deep’ y ‘regex’. var $validate = array( 'myCheckbox' => array( 'rule' => array('boolean'). 'message' => 'Valor incorrecto en myCheckbox' ) ). El ‘type’ puede ser ‘fast’. Acepta tres parámetros: ‘type’.1.4. 4. 4.4. los enteros 0 o 1 y las cadenas "0" o "1". 'on' => 'create' ) ). Soporta “is greater”. 'message' => 'El número de tarjeta de crédito que ha suministrado no es válido. 'maestro').' ) ). 18).• voyager Si ‘type’ es dejado en ‘fast’. se validan los datos contra el formato numérico de las principales tarjetas de crédito. 4. '>='. var $validate = array( 'age' => array( 'rule' => array('comparison'. “less or equal”. 'message' => 'Debe tener al menos 18 años para calificar.6 comparison Esta regla es usada para comparar valores numéricos. false. El índice ‘regex’ permite indicar una expersión regular propia que será usada para validar el número de tarjeta de credito. .' ) ).4. “is less”. la validación usará el algoritmo de Luhn para tarjetas de crédito (http://en. null). A continuación algunos ejemplos: var $validate = array( 'age' => array( 'rule' => array('comparison'. Si es verdadero. También se puede dejar ‘type’ como un arreglo con todos los tipos de validaciones que se quiere satisfacer. y “not equal”.' ) ). Por defecto el valor se asume como falso. 'greater or equal'. “equal to”. 'message' => 'Debe tener al menos 18 años para calificar. El índice ‘deep’ debería dejarse con un valor booleano.org/wiki/Luhn_algorithm). 18).wikipedia. “is less”.1. “greater or equal”. array('visa'. var $validate = array( 'ccnumber' => array( 'rule' => array('cc'. podrias considerar aceptar una amplia variedad de formatos de fechas y luego convertirlos. se usará el índice por defecto ‘ymd’. El valor del parámetro puede ser uno de los siguientes formatos: • ‘dmy’ por ejemplo 27-12-2006 o 27-12-06 (los separadores pueden ser espacio. slash) Si no especifica ningún índice. Se puede pasar un parámetro para especificar la cantidad de dígitos requeridos después del punto decimal. guion.4. Un único parámetro (que puede ser un arreglo) puede ser pasado y que será usado para verificar el formato de la fecha indicada. guion. var $validate = array( 'born' => array( 'rule' => 'date'. punto. slash) • ‘ymd’ por ejemplo 2006-12-27 or 06-12-27 (los separadores pueden ser espacio. Mientras que muchos almacenes de datos (motores de bases de datos) requieren cierto formato de datos. slash) • ‘dMy’ por ejemplo 27 December 2006 o 27 Dec 2006 • ‘Mdy’ por ejemplo December 27. mejor. 2006 o Dec 27.'. guion. guion. slash) • ‘mdy’ por ejemplo 12-27-2006 or 12-27-06 (los separadores pueden ser espacio.1. 'message' => 'Ingrese una fecha válida usando el formato AA-MM-DD. Entre más trabajo puedas hacer por tus usuarios.4.7 date Esta regla asegura que los datos enviados esten en un formato de fecha válido. 'allowEmpty' => true ) ).1. 4.8 decimal Esta regla asegura que el dato es un número decimal válido. 2006 (la coma es opcional) • ‘My’ por ejemplo (December 2006 o Dec 2006) • ‘my’ por ejemplo 12/2006 o 12/06 (los separadores pueden ser espacio. el dato será validado como un número de punto flotante científico. punto. que causará que la validación no sea satisfecha si es que no se encuentra ningún dígito después del punto decimal. en vez de forzar a los usuarios a ingresar cierto formato. punto.4. punto. . Si no se pasa ningún parámetro. 'message' => 'Por favor indique una dirección de correo electrónico válida. 4.' ) ). 4.jpg o . true). 'message' => 'El valor debe ser el string cake' ) ). var $validate = array( 'food' => array( 'rule' => array('equalTo'.var $validate = array( 'price' => array( 'rule' => array('decimal'. var $validate = array( 'email' => array( 'rule' => array('email'. y del mismo tipo que el valor indicado.1. Para permitir múltiples extensiones estas se deben pasar dentro de un arreglo.4. 4.11 extension Esta regla verifica que la extensión de archivo sea como . 2) ) ).1. 'cake'). .4.1.4.9 email Esta regla verifica que el dato sea una dirección de correo electrónico válida. var $validate = array('email' => array('rule' => 'email')).png. Al pasar un valor booleano verdadero como segundo parámetro se tratará también de verificar que el host de la dirección sea válido.10 equalTo Esta regla asegura que el valor sea equivalente a. 4.' ) ). array('gif'. var $validate = array( 'clientip' => array( 'rule' => 'ip'.1. por favor usa los links y déjanos saber tu sugerencia! 4. 'message' => 'Este nombre de usuario ya ha sido asignado. 'jpeg'. no puede ser usado por ningún otro registro. 4.' ) ). var $validate = array( 'login' => array( 'rule' => 'isUnique'. 'jpg').12 file Esta sección aún tiene que ser escrita.14 isUnique El dato para este campo debe ser único.13 ip Esta regla asegura que haya sido ingresada una dirección IPv4 válida. 'png'.4.1. . 4.var $validate = array( 'image' => array( 'rule' => array('extension'. si tienes una idea de qué poner aqui. 'message' => 'Por favor indique una imágen válida.4. 'message' => 'Por favor ingrese una dirección IP válida.' ) ).1. 'left'). var $validate = array( 'login' => array( 'rule' => array('minLength'. var $validate = array( 'salary' => array( 'rule' => array('money'.15 minLength Esta regla asegura que el dato cumple con un requisito de largo mínimo. 4. 4.17 money Esta regla asegura que el valor sea una cantidad en formato monetario válido. '15').1.' ) ).4.4. var $validate = array( 'login' => array( 'rule' => array('maxLength'.4.16 maxLength Esta regla asegura que el dato siempre esté dentro del requisito de largo máximo. .4.' ) ). 'message' => 'Por favor ingrere una cantidad monetaria válida.1. '8'). 'message' => 'Los nombres de usuario deben tener un largo de al menos 8 caracteres. El segundo parámetro define dónde se ubica el símbolo: left/right (izquierda/derecha).' ) ).1. 'message' => 'Los nombres de usuario no pueden tener un largo mayor a 15 caracteres. ' ) ). var $validate = array( 'function' => array( 'allowedChoice' => array( 'rule' => array('inList'. dos o tres opciones' ) ).18 multiple Empleado para validar campos input select multiple.20 numeric Verifica si el dato ingresado es un número válido. var $validate = array( 'cars' => array( 'rule' => 'numeric'. .19 inList Esta regla asegura que el valor está dentro de un conjunto dado. 'Bar')). 4. array('in' => array('foo'. Necesita de un arreglo de valores.' ) ) ). 4. 'min' => 1. array('Foo'.4.4.1. 'message' => 'Por favor seleccione una. 'bar'). var $validate = array( 'multiple' => array( 'rule' => array('multiple'.1. El valor es válido si coincide con uno de los valores del arreglo indicado.4. "max" y "min".1. 'message' => 'Por favor indique la cantidad de vehículos. 'message' => 'Ingreso Foo o ingrese Bar.4. Soporta los paramentros "in". 'max' => 3)). Reino Unido (uk). puedes proveer una expresión regular como segundo parámetro para cubrir formatos adicionales. var $validate = array( 'phone' => array( 'rule' => array('phone'. var $validate = array( 'title' => array( 'rule' => 'notEmpty'. Alemania (de) y Bélgica (be). Italia (it).' ) ). Canada (ca).1. (us).UU. 'us') ) ). .4. Si quieres validar números telefónicos que no sean de EE.1. 4. 4. null.4.1.UU.4. null. 'message' => 'Este campo no puede quedar vacío. 'us') ) ).UU.21 notEmpty Regla básica para asegurar que un campo no este vacío.4. var $validate = array( 'zipcode' => array( 'rule' => array('postal'. Para otros formatos ZIP puedes proveer una expersión regular como segundo parámetro.22 phone Phone valida números telefónicos de EE.23 postal Postal es usado para validar códigos ZIP de EE. Dinamarca (dk). ftp(s). var $validate = array( 'ssn' => array( 'rule' => array('ssn'.24 range Esta regla asegura que el valor esté dentro de un rango dado. la regla va a verificar si el valor es un número finito válido en la actual plataforma.25 ssn Ssn valida los números de seguridad social de EE. 0.4. news. 'message' => 'Por favor ingrese un número entre 0 y 10' ) ). .26 url Esta regla verifica formatos de URL válidos. file. null.1. 10). y gopher.4.4. 4. 'us') ) ).1.1. Si no se indica un rango. Soporta los protocolos http(s). 4.01) y menor a 10 (por ejemplo 9. (us). y los Paises Bajos (nl).UU.4.99). El ejemplo de arriba aceptará cualquier valor mayor a 0 (por ejemplo 0. Para otros formatos de números de seguridad social puedes proveer una expersión regular. var $validate = array( 'number' => array( 'rule' => array('range'. var $validate = array( 'website' => array( 'rule' => 'url' ) ). o creando métodos de validación personalizados.4. 'recursive' => -1) ). con un largo mínimo de tres caracteres.2 Validación Mediante Métodos Personalizados Algunas veces revisar los datos usando expresiones regulares no es suficiente. var $validate = array( 'promotion_code' => array( 'rule' => array('limitDuplicates'. 'message' => 'Sólo letras y enteros. El ejemplo de arriba verifica si login contiene sólo letras y enteros. necesitas agregar una función de validación personalizada. 4. return $existing_promo_count < $limit. puedes definir una expresión personalizada como una regla de validación de un campo.1. como se muestra más abajo: <?php class User extends AppModel { var $name = 'User'. mínimo 3 caracteres' ) ).1. siempre podrás crear tus propias reglas de validación personalizadas.1. si quieres asegurar que un código promocional sólo pueda ser usado 25 veces. } } ?> .' ) ). $limit){ $existing_promo_count = $this->find( 'count'. var $validate = array( 'login' => array( 'rule' => array('custom'. array('conditions' => $data.}$/i').5. 25). Por ejemplo. Hay dos maneras de hacer esto: definiendo expresiones regulares personalizadas.5 Reglas de Validación Personalizadas Si hasta el momento no has encontrado lo que buscabas. function limitDuplicates($data. 4. '/^[a-z0-9]{3.1 Validación Personalizada Mediante Expresiones Relugares Si la técnica de validación que necesitas usar puede ser completada usando expresiones regulares.5. 'message' => 'Este código ha sido usado demasiadas veces. Esto significa que puedes sobreescribir métodos de validación existentes (como por ejemplo alphaNumeric()) en el nivel de aplicación (agregando el método a AppModel). Notar que los métodos del model/behavior son verificados primero. agrega elementos extra al arreglo ‘rule’ y trátalos como parámetros extra (despúes del parámetro principal $data) en tu función personalizada. Esto es diferente al método save que permite pasar los datos como un parámetro. Tu función de validación personalizada puede estar en el modelo (como en el ejemplo de arriba). . o en un behavior implementado por el modelo. habrán veces que te gustaría validar los datos sin guardarlos. También. para verificar si los datos se validan correctamente. ten en cuenta que no es necesario llamar a validates antes de llamar a save ya que save validará automáticamente los datos antes realmente de guardarlos. validationErrors // contiene el arrego Es importante notar que los datos se deben primero setear al modelo antes de poder validarlos.1. Primero. $errors = $this->ModelName->invalidFields(). Luego. podrías querer mostrar algo de información adicional al usuario antes de guardar los datos a la base de datos. que retornará true si es que se valida y false si es que no: if ($this->ModelName->validates()) { // paso la lógica de validación } else { // no paso la lógica de validadición } El método validates invoca el método invalidFields que le asignará un valor a la propiedad validationErrors del modelo. Validar datos requiere de un proceso ligeramente distinto al de sólo guardar los datos. o en el nivel de modelo. Por ejemplo. El método invalidFields también retorna los datos como su resultado. 4. antes de buscar un método en la clase Validation. Esto incluye los modelos mapeados.6 Validando datos desde el Controlador Mientras que normalmente sólo usarás el método save del modelo.Si quieres pasar parámetros a tu función de validación personalizada. debes setear los datos al modelo: $this->ModelName->set( $this->data ). usa el método validates del modelo. puedes hacer llamadas estáticas a Sanitize. Todo lo que necesitas hacer es incluír la librería del núcleo Sanitize (p. '@')). @@ echo Sanitize::paranoid($badString).// salida: scripthtml 4. Esta función elimina cualquier cosa desde $string que no sean caracteres alfanuméricos.2 Limpieza de Datos La clase Sanitize de CakePHP puede ser usada para eliminar datos maliciosos y otra información no deseada desde los datos enviados por un usuario. class MiController extends AppController { . Sanitize es una librería del núcleo.. Esto . CakePHP te protege automáticamente contra Inyección SQL si usas los métodos ORM de CakePHP (como find() y save()) y la notación de arrays apropiada (ejemplo array('campo' => $valor)) en lugar de sentencias SQL incrustadas en el código. boolean $remove = false) Este método prepara los datos enviados por un usuario para desplegarlos dentro de HTML.4. array $allowedChars).1 paranoid paranoid(string $string.ej.2 html html(string $string. 4.. } Una vez hecho eso.2. Para la limpieza de datos contra XSS generalmente es mejor guardar el HTML sin tratar en la base de datos y posteriormente limpiar los datos en el momento de mostrarlos.. por lo que puede ser usada en cualquier parte de tu código. . array(' '.. antes de la definición de la clase controlador): App::import('Sanitize').// salida: scripthtml echo Sanitize::paranoid($badString. pero probablemente será mejor usada en los controladores y modelos. $badString = ". Esta función no eliminará ciertos caracteres al pasarlos en el arreglo $allowedChars.:<script><html>< // >@@#".2. php.3 escape escape(string $string.>. • Se reemplazan las barras invertidas ingresadas por el usuario por barras invertidas confiables. el contenido HTML detectado es eliminado en vez de mostrado como entidades HTML.es especialmente útil si no quieres que los usuarios quiebren tus layouts o inserten imágenes o scripts dentro de las páginas HTML. Las siguientes operaciones de limpieza son realizadas en cada elemento del arreglo (recursivamente): • Los espacios raros (incluyendo 0xCA) son reemplazados con espacios regulares.4 clean Sanitize::clean(mixed $data. por ejemplo).& lt. string $connection) Usado para escapar sentencias SQL agregándole barras invertidas.fontsize=".. mixed $options) Esta función es un limpiador multi-propósito y de potencia industrial. true).. $connection es el nombre de la base de datos para la cual escapar el string.99". Si la opción $remove se deja en true. //salida:<. 4. echo Sanitize::html($badString). según el nombre definido en app/config/database. La función recibe un arreglo (o string) y retorna su versión limpia. 4. • Se agregan barras para SQL (sólo llama a la función sql explicada anteriormente)./script>....2./font>.#FF0000".<. dependiendo de la configuración de magic_quotes_gpc del sistema.color=". .</script>'.// salida: HEY.HEY<. • Verificación doble de caracteres especiales y remoción de retornos de carro para una mayor seguridad SQL. $badString = '<font size="99" color="#FF0000">HEY</font><script>..2..script>. y sirve para ser usado en arreglos (como $this->data. echo Sanitize::html($badString. [array parameters]). Al llamar este método se mostrará una página de error al usuario y se detendrá cualquier tipo de procesamiento en tu aplicacion. Típicamente vas a usar set() para dejar disponibles sus parámetros en la vista y luego mostrar (render) un fichero tipo vista desde el directorio app/views/errors.4. puedes usar el método: $this->cakeError(<string errorType>. es común detener el procesamiento y mostrar una página de error al usuario. pero en estos momentos (escritura de este manual). Digamos que tenemos una aplicación que escribe cierta cantidad de ficheros a disco y que es . <?php class AppError extends ErrorHandler { } ?> Se pueden implementar administradores (handlers) para nuevos tipos de error agregando métodos a esta clase. De manera alternativa. Todo esto comienza a ser mucho más útil al extender el administrador de errores para que use tus propios tipos de error. Uno que es más útil para el desarrollador de aplicaciones es el viejo y querido error 404.php con la siguiente definición. Simplemente crea un nuevo método con el nombre que quieres usar como tu tipo de error. puedes hacer que la página reporte que el error fue en una URL específica pasando el parámetro url: $this->cakeError('error404'. CakePHP pre-define un conjunto de tipos de error.url')). Puede ser llamado sin ningún parámetro de la siguiente manera: $this->cakeError('error404'). array('url' => 'some/other. Para ahorrarte tener que codificar el manejo de esto en cada uno de tus controladores y componentes. la mayoría son realmente útiles para el mismo framework. Los administradores de error customizados son principalmente como acciones de un controlador. Crea un fichero app/app_error.3 Manejo de Errores En caso de un error irrecuperable en tu aplicación. $this->__outputMessage('cannot_write_file'). No quieremos agregar código para esto en diferentas partes de la aplicación. establece el segundo parámetro a true. Por defecto la función muestra la línea y el archivo donde se origina. . esta trabaja similar a la función de PHP print_r().apropiado mostrale al usuario los errores de escritura. Agrega un nuevo método a tu clase AppError. $params['file']). La función debug() permite mostrar el contenido de una variable en diferente número de formas. $showHTML = false. } Crea la vista en app/views/errors/cannot_write_file.4. $showFrom = true) La función debug() está disponible de forma global. 4.</p> y lanza el error en tu controllador/componente $this->cakeError('cannotWriteFile'.ctp. array('file'=>'somefilename')). si quieres que la data se muestre en formato HTML amigable. así que es un buen caso para usar un nuevo tipo de error. La implementación por defecto de $this->__outputMessage() sólo mostrará la vista en views/errors/. 4. Vamos a aceptar un parámetro llamado file que será la ruta al fichero cuya escritura fallo. Primero. provee de varias herramientas para ayudar en la depuración y exponer lo que se esta ejecutando dentro de su aplicación. puedes redefinir __outputMessage($template) en tu clase AppError. Mientras que CakePHP no ofrece ninguna herramienta que se conecte directamente con ningún editor o IDE.4 Depuración La depuración es una parte necesaria e inevitable de cualquier ciclo de desarrollo. Si quieres cambiar este comportamiento.1 Depuración básica debug($var.ctp <h2>No fue posible escribir en el fichero</h2> <p>No se pudo escribir el fichero <?php echo $file ?> en el disco. function cannotWriteFile($params) { $this->controller->set('file'. //despliegue Car:: Car::colour = 'red' Car::make = 'Toyota' Car::model = 'Camry' Car::mileage = '15000' Car::acclerate() Car::decelerate() Car::stop() log($var. 3 ) //objeto simple $car = new Car(). Desplegará todas las propiedades y métodos (si existen) de la variable que se indique.2. 4. //outputs array( 1. dump($var) Dump muestra el contenido de una variable.4. 2.3).2 Usando la clase Debugger Para usar el depurador hay que primero asegurarse que Configure::read('debug') este seteado a un valor mayor a 0.La salida de esta función solo se muestra si la variable debug del core (app/config/core. Debugger::dump($foo). Debugger::dump($car).php. $foo = array(1. línea 43) se ha establecido a un valor mayor que 0. $level = 7) . LIBS. 2) ). 321. que a su vez llamó a Dispatcher::_invoke(). index.php. line 48 Dispatcher::_invoke() . Esta información es útil cuando se trabaja con operaciones recursivas.CORE/cake/dispatcher. Notar que el directorio app/tmp (y su contenido) debe tener permisos de escritura para el servidor web para que log() funcione correctamente. Al leer la traza de ejecución desde abajo hacia arriba se muestra el orden de las funciones actualmente en ejecución (stack frames). ya que se identifican las funciones que estaban en ejecución al momento de llamar a trace(). line 84 Arriba se muestra una traza de ejecución generada llamando a Debugger::trace() desde una acción de un controlador. line 237 [main] . destaca la línea número $line con la cantidad de $context líneas a su alrededor. $line.DS. //se despliegará lo siguiente Array ( [0] public</span></code> [1] => <code><span style="color: #000000"> */</span></code> [2] => <code><span style="color: #000000"> function excerpt($file. //despliege PostsController::index() . El método log() genera datos similares a los de Debugger::dump() pero los envía al archivo debug. excerpt($file.php. => <code><span style="color: #000000"> * @access . //En PostsController::index() pr( Debugger::trace() ). trace($options) Retorna la traza de ejecución actual. incluyendo desde cuál archivo y desde que línea la llamada se originó.APP/webroot/index.php.log en vez del buffer de salida. Cada línea de la traza incluye el método que fue llamado. line 265 Dispatcher::dispatch() . pr( Debugger::excerpt(ROOT.Crea un log detallado de la traza de ejecución al momento de su invocación. Luego el método _invoke() llamó a PostsController::index().php'.CORE/cake/dispatcher.'debugger.APP/controllers/downloads_controller.php.php llamó a Dispatcher::dispatch(). En el ejemplo de arriba. $context) Toma un extracto desde un archivo en $path (que es una ruta absoluta). se debe establecer Configure::debug a un valor mayor que 0. La clase Debugger sobreescribe el manejo de errores por defecto de PHP. Al igual que con todas las funciones de depuración. 4. $recursion = 0) Convierte una variable de cualquier tipo a un string para su uso en el despliegue del depurador.4. Tiene muchos métodos que pueden ser invocados de forma estática. El reporte de errores generado contiene tanto la pila de llamadas como un extracto del código donde ocurrió el error. Cuando ocurre un error. proveyendo volcado. $context = 2) {</span></code> [3] => <span class="code-highlight"><code><span style="color: #000000"> $data = $lines = array(). reemplazándolo con información de errores mucho más útil. una a la página y la otra crea una entrada en el archivo error.</span></code></span> [4] => <code><span style="color: #000000"> @explode("\n". y el enlace "Code" para ver las líneas de código causantes del error.3 Clase Debugger La clase Debugger es nueva en CakePHP 1.2. La depuración de errores está activa por defecto en CakePHP.</span></code> ) $data = Aunque este método es usado internamente. el depurador genera dos salidas de información. Haga clic en el enlace "Error" para ver la pila de llamadas. file_get_contents($file)).$line. trazabilidad. y puede ser usado también en tus propios Debuggers. invoke($debugger) Reemplazar el Debugger de CakePHP con un nuevo Administrador de Errores. Este método también es usado mucho por Debugger para conversiones internas de variables. te puede ser práctico si estas creando tus propios mensajes de error o entradas de log en ocasiones especiales. ofrece muchas opciones para obtener información de depuración. . y funciones de gestión de errores. exportVar($var.log. Para obtener más información. para escribir su mensaje en el log de depuración ubicado en app/tmp/logs/debug. página completa o elemento de caché.log.log 2007-11-02 10:22:02 Error: Algo que no hace nada! El segundo parámetro es usado para definir el tipo de log con el se quiere escribir el mensaje.1 Uso de la función log La función log() toma dos parámetros. que es el ancestro común de la mayoría de las clases CakePHP. consulte cómo deshabilitar la memoria caché del navegador. el valor por defecto es LOG_ERROR. 4... el cual escribe en el log de errores previamente mensionado. puede guardar sus datos. La grabación (registro) puede ser también una manera de descubrir es que ocurrió en su solicitud en cualquier momento. Se puede establecer este segundo parámetro a LOG_DEBUG. usted necesitará algún tiempo para grabar datos en el disco para saber lo que pasa.caché a cualquier cosa. //Ejecutando esto dentro de una clase CakePHP: $this->log("Algo que no hace nada!").6 Logging Aunque los ajustes de Configuración de la clase desde el corazón de CakePHP realmente puede ayudarle a ver qué pasa en el fondo. Controlador. el almacenamiento en caché de consultas o la memoria caché o también la función . Por defecto. Para más información.4. . //El resultado de esto se agrega a app/tmp/logs/error.6. la depuración puede ser difíícil.log: .5 Caching Almacenamiento en caché se puede hacer en diferentes niveles en una aplicación de CakePHP. Si el contexto es una clase CakePHP (Modelo. este mensaje de error es escrito en el log de errores ubicado en app/tmp/logs/error. En un mundo cada vez más dependientes de tecnologías como SOAP y AJAX. El primero es el mensaje que se desea escribir en el archivo de log.la función log () es un elemento de la clase Object. ¿Qué términos de búsqueda se utilizaron? ¿Qué tipo de errores de mis usuarios que han visto? ¿Con qué frecuencia se ejecuta una consulta? En CakePHP la grabación (registro) es fácil . 4. lo que sea). Componente . Si no se suministra. Como alternativa. 'A special message for activity logging'). If a type is not supplied. 4. LOG_ERROR is used which writes to the error log.2 Using the default FileLog class While CakeLog can be configured to write to a number of user configured logging adapters. using the second parameter.log) 2007-11-02 10:22:02 Error: Un mensaje de depuración. The type of log message being written determines the name of the file the message is stored in.log) 2007-11-02 10:22:02 Activity: A special message for activity logging in this being appended to app/tmp/logs/activity. You can configure additional/alternate FileLog locations using CakeLog::config(). The default logging configuration will be used any time there are no other logging adapters configured.log (rather than The configured directory must be writable by the web server user in order for logging to work correctly. //El resultado de esto se agrega a app/tmp/logs/debug. The default log location is app/tmp/logs/$type. LOG_DEBUG).log 2007-11-02 10:22:02 Error: Something didn't work! You can specify a custom log names. The default built-in FileLog class will treat this log name as the file you wish to write logs to. //called statically CakeLog::write('activity'.log (en lugar de error. .2.log //Executing this inside a CakePHP class: $this->log("Something didn't work!").6. it also comes with a default logging configuration. //Results error. //Results in this being appended to app/tmp/logs/error. This configuration is identical to how CakeLog behaved in CakePHP 1.///Ejecutando esto dentro de una clase CakePHP: $this->log('Un mensaje de depuración.'. El usuario del servidor web debe poder escribir en el directorio app/tmp para que el log pueda funcionar correctamente. Once a logging adapter has been configured you will need to also configure FileLog if you want file logging to continue. As its name implies FileLog writes log messages to files. )).php. If for example you had a database logger called DataBaseLogger. or part of plugins. //for plugin called LoggingPack CakeLog::config('otherFile'. array( 'engine' => 'LoggingPack. )). All of the other configuration properties are passed to the log stream's constructor as an array.. 'path' => '/path/to/custom/place/' )).. . array( 'engine' => 'DataBaseLogger'. As part of a plugin it would be placed in app/plugins/my_plugin/libs/log/data_base_logger. } } ... 'model' => 'LogEntry'. Configuring our DataBaseLogger would look like //for app/libs CakeLog::config('otherFile'. array( 'engine' => 'FileLog'.DataBaseLogger'. When configured CakeLog will attempt to load Configuring log streams is done by calling CakeLog::config().6.. 'model' => 'LogEntry'. When configuring a log stream the engine parameter is used to locate and load the log handler. As part of your application it would be placed in app/libs/log/data_base_logger. 4.FileLog accepts a path which allows for custom paths to be used.3 Creating and configuring log streams Log stream handlers can be part of your application.php.. class DataBaseLogger { function __construct($options = array()) { //. . CakeLog::config('custom_path'. php. It should be noted that you will encounter errors when trying to configure application level loggers from app/config/core. This write method must take two parameters $type. This is because paths are not yet bootstrapped. $val).7 Testing A partir de CakePHP 1.CakePHP has no requirements for Log streams other than that they must implement a write method. warning. En esta sección discutiremos cómo preparar la aplicación para testing y cómo construir y ejecutar tus tests. 4. false). Would log only warning and fatal errors. core values are error. 4.5 Error logging Errors are now logged when Configure::write('debug'. Once a log stream has been dropped it will no longer receive messages. The return of configured() is an array of all the currently configured streams. $message in that order. will disable error logging when debug = 0.1 Preparándose para el testing ¿Preparado/a para empezar a hacer test? ¡Bien! ¡Vamos allá entonces! . info and debug. You can remove streams using CakeLog::drop($key).6. 4. $type is the string type of the logged message. 0). In addition you can define your own types by using them when you call CakeLog::write. You can use Configure::write('log'. By default all errors are logged.php to ensure classes are properly loaded. Configure::write('log'. Configuring of loggers should be done in app/config/bootstrap.7.4 Interacting with log streams You can introspect the configured streams with CakeLog::configured().2 disponemos de soporte para un completo entorno de testing incorporado en CakePHP.. Este entorno es una extensión del entorno SimpleTest para PHP. 4. to control which errors are logged when debug is off. E_WARNING).6. Setting Configure::write('log'. Intenta ejecutar alguno de los grupos de test del núcleo haciendo click en el enlace correspondiente.1 Installing SimpleTest El entorno de testing provisto con CakePHP 1. ¡Recuerda tener el nivel de DEBUG al menos a 1 en tu archivo app/config/core. 'persistent' => false. todas las tablas serán creadas en esta base de datos. ya estás listo/a para empezar a escribir tests! .net/ Consigue la última versión y descomprime el código en tu carpeta cake/vendors. 0 fails and 0 exceptions. Si la base de datosd e test está disponible y CakePHP puede conectarse a ella.".php.cake. Ahora deberías tener un directorio vendors/simpletest con todos los archivos y carpetas de SimpleTest dentro. ¡Felicidades.sourceforge.4.2 se distribuye con un gran paquete de test-cases sobre la funcionalidad del núcleo de CakePHP Puedes acceder a estos test navegando a http://your. o en tu carpeta app/vendors.php antes de ejecutar cualquier test! Si no tienes una conexión de base de datos para test definida en app/config/database.7.php dependiendo de como sea la disposición específica de tu aplicación. 'password' => 'dbpassword'.2 Ejecutando los test-cases incorporados CakePHP 1. según tus preferencias.2 está construido sobre el entorno de testing SimpleTest.1. Ejecutar un test puede llevar un rato. 'host' => 'dbhost'.domain/cake_folder/test. Lo puedes encontrar aquí: http://simpletest. 'database' => 'databaseName' ). 4. 'login' => 'dblogin'. SimpleTest no se distribuye con la instalación por defecto de CakePHP por lo que debemos descargarlo primero.1. pero deberías ver algo parecido a "2/2 test casese complete: 49 passes.7. Puedes crear una conexión de base de adtos $test para que contenga sólo las tablas de test como la que te mostramos debajo: var $test = array( 'driver' => 'mysql'. las tablas de test se crearán con un prefijo test_suite_. en el cual automatizas el trabajo de evaluar tu aplicación mediante la navegación por las páginas. Si esta conexión no es utilizable.2 Creando fixtures Cuando se crea un fixture se deben definir 2 cosas: 1) cómo se crea la tabla (que campos serán parte de la tabla) 2) cómo se guardarán los registros en la tabla de prueba.3 Preparando datos de prueba 4. Vacía las tablas fixture 5.7. Ejecuta los métodos de los test 4.7. Creamos un archivo llamado article_fixture. Elimina las tablas fixture de la base de datos 4.php . añadirá el prefijo "test_suite_" a tu propio prefijo para las tablas (si es que hay alguno) para evitar colisiones con las tablas existentes. Luego podremos crear nuestro primer fixture. Web testing El entorno de test de CakePHP soporta dos tipos de testing. que utilizaremos para testear nuestro modelo Article.Unit testing vs. El otro tipo de testing soportado es el Web Testing.4.php. puedes usar fixtures como una forma de generar tablas temporales de datos cargados con datos de ejemplo que pueden ser utilizados por el test. Además.2 Introducción a los test .3. Crea tablas para cada una de las fixtures necesarias 2. CakePHP intenta utilizar la conexión denominada $test en tu archivo app/config/database. El beneficio de usar fixtures es que tus test no pueden de ningún modo alterar los datos de la aplicación en marcha. como pueden ser un método en un componente o una acción en un controlador. 4.1 Acerca de las fixtures Cuando pruebes código que dependa de modelos y datos. así puedes empezar a probar tu código antes de desarrollar contenido en vivo para tu aplicación. relleno de formularios. Rellena las tablas con datos. en el cual tú pruebas pequeñas partes de tu código.3. hacer clic en enlaces y demás.7. Uno es el Unit Testing. En cualquier caso.7. usará la configuración de base de datos $default y creará las tablas de test en la base de datos definida en esa configuración. CakePHP realiza los siguientes pasos durante el curso de un test case basado en fixture: 1. si es que se han proporcionado éstos en la fixture 3. 'created' => '2007-03-18 10:39:23'. 'published' => '1'. 'created' => '2007-03-18 10:43:23'. 'updated' => '200703-18 10:45:31') ). float (mapea como FLOAT). 'published' => '1'. text (mapea como TEXT). 'updated' => '200703-18 10:41:31'). 'length' => 255. 'default' => '0'. array ('id' => 3. y cómo serán definidos. integer (mapea como INT). var $fields = array( 'id' => array('type' => 'integer'. 'title' => 'Third Article'. con el siguiente código: <?php class ArticleFixture extends CakeTestFixture { var $name = 'Article'.en la carpeta app/tests/fixtures. 'updated' => 'datetime' ).) Los atributos que un campo puede tenes son los siguientes: type es el tipo de dato de CakePHP. 'null' => false). 'body' => 'Second Article Body'. Actualmente los soportados son: string (mapea como VARCHAR). 'created' => 'datetime'. timestamp (mapea como TIMESTAMP). var $records = array( array ('id' => 1. 'title' => 'Second Article'. 'published' => array('type' => 'integer'. 'body' => 'Third Article Body'. time (mapea . 'title' => 'First Article'. 'body' => 'text'.php. 'updated' => '200703-18 10:43:31'). 'created' => '2007-03-18 10:41:23'. 'published' => '1'. 'key' => 'primary'). 'body' => 'First Article Body'. } ?> Usamos $fields para indicar los campos que serán parte de la tabla. 'null' => false). 'title' => array('type' => 'string'. datetime (mapea como DATETIME). array ('id' => 2. El formato que se usa para definir los campos es el mismo que usamos en la funcion generateColumnSchema() definida en el motor de base de datos de Cake (por ejemplo en dbo_mysql. pero puedes hacerlo cambiandola para que sea: . null setea true o false. length setea el tamaño del campo. Solo ten en cuenta que cada registro del array $records debe tener una key para cada campo del array $fields.3 Importar información de tabla y registros Tu aplicación puede tener ya modelos funcionando con datos reales asociados. solo especifica el valor de ese campo como nulo (NULL true). Si un campo para un registro en particular necesita tener el valor nulo.7. sin embargo necesita un poco más de expilcación. Sería entonces un esfuerzo doble tener que definir la tabla y/o los registros en tus fixtures. La expresión anterior no importa registros. y puedes decidir probar tu modelo con esos datos. Comencemos con un ejemplo.como TIME). cambiamos la fixture de ejemplo que dimos en la sección anterior (app/tests/fixtures/article_fixture. El formato es bastante simple. } ?> Esta sentencia le dice a la test suite que importe tu definición de tabla de la tabla asociada al modelo llamado Article. Por suerte. var $import = 'Article'. Puedes usar cualquier modelo disponible en tu aplicación. si no se permiten nulos va false default el valor por defecto del campo. Si puede ser nulo indicamos true. 4.php) a: <?php class ArticleFixture extends CakeTestFixture { var $name = 'Article'. Finalmente podemos setear un conjunto de registros que seran cargados luego de que la tabla de testeo se crea. date (mapea como DATE). y binary (mapea como BLOB) key setea el campo como primary para hacerlo auto-incrementable (AUTO_INCREMENT).3. Asumiento que tienes un modelo llamado Article disponible en tu aplicación (que se corresponde con una tabla llamada articles). y clave primaria (PRIMARY KEY) de la tabla. hay una forma de hacer que la definición de la tabla y/o los registros para una fixture en particular vengan de un modelo o una tabla ya existentes. <?php class ArticleFixture extends CakeTestFixture { var $name = 'Article'. } ?> Ya que se usa tu conexión a la base de datos. 'connection' => 'other'). 'records' => true). puedes especificar que tu importación consistirá en leer la información de la tabla. var $import = array('model' => 'Article'. pero tener tus registros definidos directamente en la fixture. si hay algún prefijo de tabla declarado. var $import = array('table' => 'articles'). este será usado automáticamente al recabar la información de tabla. Por ejemplo: . Si quieres cambiar la conexión sólo tienes que hacer: <?php class ArticleFixture extends CakeTestFixture { var $name = 'Article'. } ?> Naturalmente puedes importar tus definiciones de tabla de un modelo o tabla existente. Los dos fragmentos anteriores no importan registros de la tabla. } ?> Esto importará la definición de una tabla llamada 'articles' usando tu conexión de base de datos denominada 'default'. } ?> Si. cambialo a: <?php class ArticleFixture extends CakeTestFixture { var $name = 'Article'. como se mostraba en la sección anterior. tienes una tabla creada pero no un modelo disponible para ella. Por ejemplo: <?php class ArticleFixture extends CakeTestFixture { var $name = 'Article'. por otro lado. 'records' => true). Para forzar a la fixture a importar también los registros. var $import = array('table' => 'articles'. var $import = array('table' => 'articles'. php en lugar de sólo . 'body' => 'First Article Body'. Cuando se crea un caso test. que contiene una afirmación) debería comenzar con test. 'updated' => '2007-03-18 10:41:31'). array ('id' => 3. var $records = array( array ('id' => 1.php (dependiendo de cómo se ve específicamente tu configuración) y haciendo clic en App casos de test. 'published' => '1'. 4. array ('id' => 2. 'title' => 'Third Article'. var $import = 'Article'. haga clic en el enlace a su archivo. .test. } ?> 4. 2.php. Los nombres de estos archivos deben terminar con un . 'created' => '2007-03-18 10:39:23'. puede ejecutarce por medio del navegador en la siguiente dirección http://tu. 'published' => '1'.<?php class ArticleFixture extends CakeTestFixture { var $name = 'Article'. El nombre de cualquier método que contenga un test (por ejemplo. 'created' => '2007-03-18 10:41:23'. 'title' => 'First Article'. y a continuación. 'body' => 'Second Article Body'. 3. 'updated' => '2007-03-18 10:43:31').7. 'title' => 'Second Article'. 'body' => 'Third Article Body'.4 Creando los tests En primer lugar. 'created' => '2007-03-18 10:43:23'.dominio. Las clases que contienen los tests debe extender o heredar de CakeTestCase o CakeWebTestCase. revisar una serie de normas y directrices para los tests: 1. 'published' => '1'. 'updated' => '2007-03-18 10:45:31') ). Los archivos de PHP que contiene los tests deben estar en : app/tests/cases/[algun_ archivo]. como en testPublished().cake/carpeta_cake/test. which looks like this: <?php class Article extends AppModel { var $name = 'Article'. startCase() called before a test case is started.7. endTest($method) Called just after a test method has completed. endCase() called after a test case has run. and/or before or after your entire CakeTestCase.4. startTest($method) Called just before a test method is executed.5 Testing models 4.1 Creating a test case Let's say we already have our Article model defined on app/models/article.published' => 1 ).4. after($method) Announces the end of a test method. end() Last method called in a test case. 4. function published($fields = null) { $params = array( 'conditions' => array( $this->name .1 CakeTestCase Callback Methods If you want to sneak in some logic just before or after an individual CakeTestCase method. the following callbacks are available: start() First method called in a test case. .5. before($method) Announces the start of a test method.7.php.7. '. so we have to start by loading our parent model (in this case the Article model which we already defined).article' ).test. In variable $fixtures we define the set of fixtures that we'll use. . c and d. CakePHP Models will only use the test_suite DB config if they rely on fixtures in your testcase! Since we also want to reuse all our existing model code we will create a test model that will extend from Article. } ?> We have created the ArticleTestCase. to test some functionality in the model. set $useDbConfig and $name appropiately. In ATestCase you will have to include fixtures for a. with the following contents: <?php App::import('Model'. We now want to set up a test that will use this model definition. class ArticleTestCase extends CakeTestCase { var $fixtures = array( 'app.'Article'). but through fixtures. CakePHP test suite loads a very minimum set of files (to keep tests isolated). b. For example: A hasMany B hasMany C hasMany D. If your model is associated with other models.'fields' => $fields ).php in your app/tests/cases/models directory. Setting $useDbConfig to this configuration will let CakePHP know that this model uses the test suite database connection. you will need to include ALL the fixtures for each associated model even if you don't use them. } } ?> return $this->find('all'. and then inform the test suite that we want to test this model by specifying which DB configuration it should use. Let's now create a file named article. CakePHP test suite enables a DB configuration named test_suite that is used for all models that rely on fixtures.$params). 'Form'.php so it now looks like this: <?php App::import('Model'. We start by creating an instance of our fixture based Article model. 'Html'). and then run our published() method. $result = $this->Article->published(array('id'. Edit the file app/tests/cases/models/article.1 Creando un test case Digamos que tienes un típico controlador de artículos.6. 'title' => 'First Article' array('Article' => array( 'id' => 2. )). $this->assertEqual($result.) We test that the result equals our expectation by using the assertEqual method. $expected = array( array('Article' => array( 'id' => 1. 'title' => 'Second Article' ).5. 'title')). See the section Creating Tests for information on how to run the test. 4. } array('Article' => array( 'id' => 3. y que se parece a éste: <?php class ArticlesController extends AppController { var $name = 'Articles'. } ?> You can see we have added a method called testPublished(). } if (!empty($short)) { $result = $this->Article->findAll(null. . 'Article'). $expected). 'title' => 'Third Article' )) )). con su correspondiente modelo. class ArticleTestCase extends CakeTestCase { var $fixtures = array( 'app.test.7. In $expected we set what we expect should be the proper result (that we know since we have defined which records are initally populated to the article table. var $helpers = array('Ajax'. function index($short = null) { if (!empty($this->data)) { $this->Article->save($this->data).2 Creating a test method Let's now add a method to test the function published() in the Article model. function testPublished() { $this->Article =& ClassRegistry::init('Article').6 Testing controllers 4.4. array('id'.7.7.article' ). debug($result). debug(htmlentities($result)). debug($result). 'title' => 'New Article'. } function testIndexPostFixturized() { $data = array('Article' => array('user_id' => 1. 'published' => 1. } function testIndex() { $result = $this->testAction('/articles/index'). } $this->set('title'. array('fixturize' => true)). } ?> } Crea un archivo llamado articles_controller. $method . array('return' => 'vars')). } function endTest($method) { echo '<hr />'.'title')). } else { $result = $this->Article->findAll(). } function testIndexShortGetViewVars() { $result = $this->testAction('/articles/index/short'. 'slug'=>'new-article'. } function testIndexFixturized() { $result = $this->testAction('/articles/index/short'. } function endCase() { echo '<h1>Terminado Test Case</h1>'. 'body' => 'New .php y pon lo siguiente dentro: <?php class ArticlesControllerTest extends CakeTestCase { function startCase() { echo '<h1>Comenzando Test Case</h1>'. debug($result). $result). debug($result). array('return' => 'render')). } function testIndexShortGetRenderedHtml() { $result = $this->testAction('/articles/index/short'.test. } function startTest($method) { echo '<h3>Comenzando método ' . '</h3>'. $this->set('articles'. } if (isset($this->params['requested'])) { return $result. } function testIndexShort() { $result = $this->testAction('/articles/index/short'). 'Articles'). Body')); $result = $this->testAction('/articles/index', array('fixturize' => true, 'data' => $data, 'method' => 'post')); debug($result); } } ?> 4.7.6.2 El método testAction La novedad aquí es el método testAction. El primer argumento de este método es la URL "en formato Cake" de la acción del controlador que se quiere probar, como en '/articles/index/short'. El segundo argumento es un array de parámetros, consistente en: return Indica lo que se va a devolver. Los valores válidos son: • 'vars' - Obtienes las variables de la vista disponibles tras ejecutar la acción • 'view' - Obtienes la vista generada, sin layout • 'contents' - Obtienes todo el html de la vista, incluyendo layout • 'result' - Obtienes el valor de retorno de la acción como cuando se usa $this>params['requested']. El valor por defecto es 'result'. fixturize Ponlo a true si quieres que tus modelos se "auto-simulen" (de modo que las tablas de la aplicación se copian, junto con los registros, para que al probar las tablas si cambias datos no afecten a tu aplicación real.) Si en 'fixturize' pones un array de modelos, entonces sólo esos modelos se auto-simularán mientras que los demás utilizarán las tablas reales. Si quieres usar tus archivos de fixtures con testAction() no uses fixturize, y en su lugar usa las fixtures como harías normalmente. method Ajustalo a 'post' o 'get' si quieres pasarle datos al controlador data Los datos que se pasarán. Será un array asociativo consistente en pares de campo => valor. Échale un vistazo a function testIndexPostFixturized() en el case test de arriba para ver cómo emulamos pasar datos de formulario como post para un nuevo artículo. 4.7.6.3 Pitfalls If you use testAction to test a method in a controller that does a redirect, your test will terminate immediately, not yielding any results. See https://trac.cakephp.org/ticket/4154 for a possible fix. 4.7.7 Testing Helpers Since a decent amount of logic resides in Helper classes, it's important to make sure those classes are covered by test cases. Helper testing is a bit similar to the same approach for Components. Suppose we have a helper called CurrencyRendererHelper located in app/views/helpers/currency_renderer.php with its accompanying test case file located in app/tests/cases/helpers/currency_renderer.test.php 4.7.7.1 Creating Helper test, part I First of all we will define the responsibilities of our CurrencyRendererHelper. Basically, it will have two methods just for demonstration purpose: function usd($amount) This function will receive the amount to render. It will take 2 decimal digits filling empty space with zeros and prefix 'USD'. function euro($amount) This function will do the same as usd() but prefix the output with 'EUR'. Just to make it a bit more complex, we will also wrap the result in span tags: <span class="euro"></span> Let's create the tests first: <?php //Import the helper to be tested. //If the tested helper were using some other helper, like Html, //it should be impoorted in this line, and instantialized in startTest(). App::import('Helper', 'CurrencyRenderer'); class CurrencyRendererTest extends CakeTestCase { private $currencyRenderer = null; //Here we instantiate our helper, and all other helpers we need. public function startTest() { $this->currencyRenderer = new CurrencyRendererHelper(); } //testing usd() function. public function testUsd() { $this->assertEqual('USD 5.30', $this->currencyRenderer->usd(5.30)); //We should always have 2 decimal digits. $this->assertEqual('USD 1.00', $this->currencyRenderer->usd(1)); $this->assertEqual('USD 2.05', $this->currencyRenderer->usd(2.05)); //Testing the thousands separator $this->assertEqual('USD 12,000.70', $this->currencyRenderer>usd(12000.70)); } } Here, we call usd() with different parameters and tell the test suite to check if the returned values are equal to what is expected. Executing the test now will result in errors (because currencyRendererHelper doesn't even exist yet) showing that we have 3 fails. Once we know what our method should do, we can write the method itself: <?php class CurrencyRendererHelper extends AppHelper { public function usd($amount) { return 'USD ' . number_format($amount, 2, '.', ','); } } Here we set the decimal places to 2, decimal separator to dot, thousands separator to comma, and prefix the formatted number with 'USD' string. Save this in app/views/helpers/currency_renderer.php and execute the test. You should see a green bar and messaging indicating 4 passes. 4.7.8 Probando componentes Supongamos que queremos hacer test a un componente llamado TransporterComponent, el cual usa un modelo llamado Transporter para proporcionar funcionalidad a otros controladores. Utilizaremos cuatro archivos: • Un componente llamado Transporters que se encuentra en app/controllers/components/transporter.php • Un modelo llamado Transporte que está en app/models/transporter.php • Una fixture llamada TransporterTestFixture situada en app/tests/fixtures/transporter_fixture.php • El código para el test, en app/tests/cases/transporter.test.php 4.7.8.1 Initializing the component Ya que CakePHP desaliante importar modelos directamente en los componentes necesitamos un controlador para acceder a los datos en el mmodelo. Si el método startup() del componente tiene este aspecto: public function startup(&$controller){ $this->Transporter = $controller->Transporter; } entonces podemos simplemente crear una clase sencilla: class FakeTransporterController {} y asignarle valores dentro de ella como aquí: $this->TransporterComponentTest = new TransporterComponent(); $controller = new FakeTransporterController(); $controller->Transporter = new TransporterTest(); $this->TransporterComponentTest->startup(&$controller); 4.7.8.2 Creando un método de prueba Simplemente crea una clase que extienda CakeTestCase y ¡comienza a escribir tests! class TransporterTestCase extends CakeTestCase { var $fixtures = array('transporter'); function testGetTransporter() { $this->TransporterComponentTest = new TransporterComponent(); $controller = new FakeTransporterController(); $controller->Transporter = new TransporterTest(); $this->TransporterComponentTest->startup(&$controller); $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Sweden"); $this->assertEqual($result, 1, "SP is best for 1xxxx-5xxxx"); $result = $this->TransporterComponentTest->getTransporter("41234", "Sweden", "44321", "Sweden"); $this->assertEqual($result, 2, "WSTS is best for 41xxx-44xxx"); $result = $this->TransporterComponentTest->getTransporter("41001", "Sweden", "41870", "Sweden"); $this->assertEqual($result, 3, "GL is best for 410xx-419xx"); $result = $this->TransporterComponentTest->getTransporter("12345", "Sweden", "54321", "Norway"); $this->assertEqual($result, 0, "Noone can service Norway"); } } 4.7.9 Web testing - Testeando las vistas La mayoria, si no es que lo son todos, los proyectos CakePHP son aplicaciones web. Aunque el testeo unitario es una excelente manera de testear pequeñas porciones de nuestro código, hay ocaciones en la que querriamos hacer un testeo a gran escala. La clase CakeWebTestCase nos brinda una muy buena manera de hacer éste tipo de testing, desde el punto de vista del usuario. 4.7.9.1 About CakeWebTestCase CakeWebTestCase es una extensión directa de SimpleTest WebTestCase, sin ninguna funcionalidad extra. Toda la funcionalidad encontrada en la documentación de SimpleTest para Testeo Web (Web testing) tambien están disponibles aqui. Esto quiere decir que no se pueden usar los fixtures, y que todos los casos de testeo involucrados en un ABM (alta, baja o modificación) a la base de datos modificarán permanentemente los valores. Los resultados del Test son comparados frecuentemente con los qe tiene la base de datos, por lo tanto, asegurarse que la bd tenga los valores que se esperan, es parte del proceso de construcción del test. 4.7.9.2 Creando un test Manteniendo las convenciones de los otros tests, los archivos de testeo de vistas se deberán crear en la carpeta tests/cases/views. Claro que se podrian guardar en otra ubicación, pero siempre es bueno seguir las convenciones. Entonces, crearemos el archivo: tests/cases/views/complete_web.test.php Para escribir testeos web, deberás extender la clase CakeWebTestCase y no CakeTestCase, tal como era en los otros tests: class CompleteWebTestCase extends CakeWebTestCase Si necesitas hacer alguna inicialización antes de que comience el test, crea el constructor: function CompleteWebTestCase(){ //Do stuff here } Cuando escribes los test cases, lo primero que vas a necesitar hacer es capturar algun tipo de salida o resultado donde ver y analizar. Ésto puede ser realizado haciendo un request get o post, usando los métodos get() o post() respectivamente. A ambos métodos se le pasa como primer parámetro la url, aunque puede ser traida dinámicamente si asumimos que script de testing está en http://your.domain/cake/folder/webroot/test.php tipeando: $this->baseurl = current(split("webroot", $_SERVER['PHP_SELF'])); Entonces podremos hacer gets y posts usando las urls de Cake, por ejemplo: $this->get($this->baseurl."/products/index/"); $this->post($this->baseurl."/customers/login", $data); El segundo parámetro del método post, $data, es un array asociativo que contiene post data en el formato de Cake: $data = array( "data[Customer][mail]" => "
[email protected]", "data[Customer][password]" => "userpass"); Una vez que se hizo el request a la página, se pueden utilizar todos los mismos asserts que veniamos usando en SimpleTest. 4.7.9.3 Walking through a page CakeWebTest also gives you an option to navigate through your page by clicking links or images, filling forms and clicking buttons. Please refer to the SimpleTest documentation for more information on that. 4.7.10 Testing plugins Tests for plugins are created in their own directory inside the plugins folder. /app /plugins /pizza /tests /cases /fixtures /groups They work just like normal tests but you have to remember to use the naming conventions for plugins when importing classes. This is an example of a testcase for the PizzaOrder model from the plugins chapter of this manual. A difference from other tests is in the first line where 'Pizza.PizzaOrder' is imported. You also need to prefix your plugin fixtures with 'plugin.plugin_name.'. <?php App::import('Model', 'Pizza.PizzaOrder'); class PizzaOrderCase extends CakeTestCase { // Plugin fixtures located in /app/plugins/pizza/tests/fixtures/ var $fixtures = array('plugin.pizza.pizza_order'); var $PizzaOrderTest; function testSomething() { // ClassRegistry makes the model use the test database connection $this->PizzaOrderTest =& ClassRegistry::init('PizzaOrder'); // do some useful test here $this->assertTrue(is_object($this->PizzaOrderTest)); } } ?> If you want to use plugin fixtures in the app tests you can reference them using 'plugin.pluginName.fixtureName' syntax in the $fixtures array. That is all there is to it. 4.7.11 Miscellaneous 4.7.11.1 Customizing the test reporter The standard test reporter is very minimalistic. If you want more shiny output to impress someone, fear not, it is actually very easy to extend. By creating a new reporter and making a request with a matching output GET parameter you can get test results with a custom reporter. Reporters generate the visible output from the test suite. There are two built in reporters: Text and Html. By default all web requests use the Html reporter. You can create your own reporters by creating files in your app/libs. For example you could create the file app/libs/test_suite/reporters/my_reporter.php and in it create the following: require_once CAKE_TEST_LIB . 'reporter' . DS . 'cake_base_reporter.php'; class MyReporter extends CakeBaseReporter { //methods go here. } Extending CakeBaseReporter or one of its subclasses is not required, but strongly suggested as you may get missing errors otherwise. CakeBaseReporter encapsulates a few common test suite features such as test case timing and code coverage report generation. You can use your custom reporter by setting the output query string parameter to the reporter name minus 'reporter'. For the example above you would set output=my to use your custom reporter. 4.7.11.2 Test Reporter methods Reporters have a number of methods used to generate the various parts of a Test suite response. paintDocumentStart() Paints the start of the response from the test suite. Used to paint things like head elements in an html page. paintTestMenu() Paints a menu of available test cases. testCaseList() Retrieves and paints the list of tests cases. groupCaseList() Retrieves and paints the list of group tests. paintHeader() Prints before the test case/group test is started. paintPass() Prints everytime a test case has passed. Use $this->getTestList() to get an array of information pertaining to the test, and $message to get the test result. Remember to call parent::paintPass($message). paintFail() Prints everytime a test case has failed. Remember to call parent::paintFail($message). paintSkip() Prints everytime a test case has been skipped. Remember to call parent::paintSkip($message). paintException() Prints paintError() Prints everytime an error is raised. Remember to call parent::paintError($message). paintFooter() Prints when the test case/group test is over, i.e. when all test cases has been executed. paintDocumentEnd() Paints the end of the response from the test suite. Used to paint things like footer elements in an html page. 4.7.11.3 Grouping tests everytime there is an uncaught exception. Remember to call parent::paintException($message). If you want several of your test to run at the same time, you can try creating a test group. Create a file in /app/tests/groups/ and name it something like your_test_group_name.group.php. In this file, extend GroupTest and import test as follows: <?php class TryGroupTest extends TestSuite { var $label = 'try'; function tryGroupTest() { TestManager::addTestCasesFromDirectory($this, APP_TEST_CASES . DS . 'models'); } } ?> The code above will group all test cases found in the /app/tests/cases/models/ folder. To add an individual file, use TestManager::addTestFile($this, filename). 4.7.12 Running tests in the Command Line If you have simpletest installed you can run your tests from the command line of your application. from app/ cake testsuite help Usage: cake testsuite category test_type file - category - "app", "core" or name of a plugin - test_type - "case", "group" or "all" - test_file - file name with folder prefix and without the (test| group).php suffix Examples: cake testsuite app all cake testsuite core all cake testsuite app case behaviors/debuggable cake testsuite app case models/my_model cake testsuite app case controllers/my_controller cake testsuite core case file cake testsuite core case router cake testsuite core case set cake testsuite app group mygroup cake testsuite core group acl cake testsuite core group socket cake // cake // testsuite bugs for the plugin testsuite bugs for the plugin case models/bug 'bugs' and its test case 'models/bug' group bug bugs and its test group 'bug' Code Coverage Analysis: Append 'cov' to any of the above in order to enable code coverage analysis As the help menu suggests, you'll be able to run all, part, or just a single test case from your app, plugin, or core, right from the command line. If you have a model test of test/models/my_model.test.php you'd run just that test case by running: cake testsuite app models/my_model 4.7.13 Test Suite changes in 1.3 The TestSuite harness for 1.3 was heavily refactored and partially rebuilt. The number of constants and global functions have been greatly reduced. Also the number of classes used by the test suite has been reduced and refactored. You must update app/webroot/test.php to continue using the test suite. We hope that this will be the last time that a change is required to app/webroot/test.php. Removed Constants • CAKE_TEST_OUTPUT • RUN_TEST_LINK • BASE • CAKE_TEST_OUTPUT_TEXT • CAKE_TEST_OUTPUT_HTML These constants have all been replaced with instance variables on the reporters and the ability to switch reporters. Removed functions • CakePHPTestHeader() • CakePHPTestSuiteHeader() • CakePHPTestSuiteFooter() • CakeTestsGetReporter() • CakePHPTestRunMore() • CakePHPTestAnalyzeCodeCoverage() • CakePHPTestGroupTestList() • CakePHPTestCaseList() These methods and the logic they contained have been refactored/rewritten into CakeTestSuiteDispatcher and the relevant reporter classes. This made the test suite more modular and easier to extend. Removed Classes • HtmlTestManager • TestManager::getGroupTestList() is no longer static. be sure to update those tools with the new formatting. exceptions. Modified methods/classes The following methods have been changed as noted. • TestManager::runAllTests() is no longer static. • TestManager::runGroupTest() is no longer static. functionality has been moved to . If you have other tools built upon the testsuite console. CodeCoverageManager changes • CodeCoverageManager::start()'s CodeCoverageManager::init() • CodeCoverageManager::start() now starts coverage generation.• TextTestManager • CliTestManager These classes became obsolete as logic was consolidated into the reporter classes. and failures from the testsuite console tool have been updated to remove redundant information and increase readability of the messages. • TestManager::runTestCase() is no longer static. • TestManager::getExtension() is no longer static. • TestManager::getTestCaseList() is no longer static. • CodeCoverageManager::stop() pauses collection • CodeCoverageManager::clear() stops and clears collected coverage reports. testsuite Console changes The output of errors. 18 y 10 son el número de caracteres entre el primero y el último carácter. Para generar archivos pot(s) todo lo que necesita es ejecutar la tarea i18n en la consola.4.8. Los archivos pot(s) en si mismos no son utilizados por CakePHP. que buscará las funciones translate utilizadas en su código y creará los archivos por usted. la primera de ellas es hacer uso de la función __() en su código. estos dos ejemplos de código son funcionalmente idénticos – ambos envían el mismo contenido al navegador. devolverá la cadena sin modificar. respectivamente. __n() etc) Con el código listo para ser multi-idioma.8 Internacionalización & Localización Una de las mejores maneras para que tus aplicaciones lleguen a un público más amplio es brindarlo en varios idiomas.1 Internacionalizando su aplicación Hay sólo unos pocos pasos para pasar de una aplicación de un solo idioma a una aplicación multi-idioma. Internacionalización se refiere a la capacidad de una aplicación para ser localiza. pero las funciones de internacionalización y localización en CakePHP lo hace mucho más fácil. son las plantillas utilizadas para crear o actualizar los archivos po. que es el modelo para todas las cadenas de texto traducibles en su aplicación. El término localización se refiere a la adaptación de una aplicación para responder a los requerimientos de un lenguaje (o cultura) específico (es decir. un "lugar"). es importante comprender algunos términos. Funciona de manera similar a otras implementaciones de Gettext (igual que otras funciones de traducción como __d(). el siguiente paso es crear su archivo pot. si no. Esto a menudo puede resultar ser una tarea de enormes proporciones. 4. que contienen las traducciones. La internacionalización y localización son a menudo abreviados como i18n y l10n. La función __() traducirá la cadena de texto que se pasa si la traducción está disponible. En primer lugar. Usted puede y debe volver a ejecutar esta tarea cada vez que se produzca algún cambio de las traducciones en el código. A continuación se muestra un ejemplo de código para una aplicación de un solo idioma: <h2>Posts</h2> Para internacionalizar su código todo lo que necesitas hacer es envolver las cadenas de texto en la función translate como se muestra a continuación: <h2><?php __('Posts') ?></h2> Si no hace nada más. Cake buscará los archivos po en . la siguiente ubicación: /app/locale/<locale>/LC_MESSAGES/<domain>. 'fre'). '/tos') 4.8. Los códigos de localización correctos son los de tres caracteres conforme al estandar ISO 639-2 aunque si crea locales regionales (en_US. por lo tanto en su carpeta locale se verá algo como esto: /app/locale/eng/LC_MESSAGES/default.language'). a menos que usted esté familiarizado con su formato. Recuerde que los archivos po son útiles para mensajes cortos.po (English) /app/locale/fre/LC_MESSAGES/default.language'.g. Existen herramientas gratuitas como PoEdit que hacen de la edición y actualización de sus archivos po una tarea fácil. } } of o // View code echo $this->element(Configure::read('Config. Por ejemplo: // App Controller Code. $this->viewPath)) { // e. use /app/views/fre/pages/tos.po (Portuguese) Para crear o editar su archivo po no se recomienda que utilice su editor favorito.language') . function beforeFilter() { $locale = Configure::read('Config. debe considerar aplicar una solución diferente.2 Localización en CakePHP Para cambiar o definir el idioma para su aplicación sólo necesita hacer lo siguiente: Configure::write('Config.014 caracteres para cada valor msgstr. en_GB. $locale .po El dominio por defecto es 'default'.ctp instead /app/views/pages/tos. . Es muy fácil crear un archivo po inválido o guardarlos con una codificación errónea (si está editando manualmente el archivo po use UTF-8 para evitar problemas).) Cake los utiliza si procede. DS .ctp $this->viewPath = $locale .po (French) /app/locale/por/LC_MESSAGES/default. etc. si necesita traducir párrafos largos. hay un límite de 1. Para crear un archivo po por primera vez es recomendable copiar el archivo pot a la ubicación correcta y cambiar la extensión. if ($locale && file_exists(VIEWS . o incluso páginas completas. DS . $this->viewPath. __($value. se utilizará la localización de la norma ISO 639-2). Es una buena idea mostrar contenido disponible en varios idiomas a partir de una URL diferente – esto hace que sea fácil para los usuarios (y los motores de búsqueda) encontrar lo que están buscando en el idioma esperado. __d('validation_errors'.php: function invalidate($field. } . etc).cardNumber'. puede utilizar un dominio diferente. como se hace en esta aplicación.po.com. true)). } La tarea i18n de la consola no será capaz de determinar el id del mensaje del ejemplo anterior. El primer parámetro de la función se utiliza como msgid definidos en los archivos . Por ejemplo: <?php echo $form->error( 'Card. array('escape' => false) ).example. __("errorCardNumber". para mostrar el contenido localizado se utiliza la función __() o una de las funciones de traducción disponibles a nivel mundial. Como se menciona en la sección anterior. fra. entre otras cosas. Hay varias formas de hacer esto. en el beforeFilter del controlador si el idioma es específico para una petición o un usuario o en cualquier otro momento si desea mostrar un mensaje en un idioma diferente. o usando un prefijo en la URL.com. true)). tal como: function invalidate($field. Recuerde que debe usar el parámetro return de la función __() si no desea que se muestre la cadena de texto directamente. $value = true) { return parent::invalidate($field. por ejemplo en el bootstrap si desea definir el idioma por defecto para su aplicación. Puede cambiar el idioma en cualquier momento. (en. ?> Si a usted le gusta tener todos los mensajes de error de validación traducidos por defecto. true). $value. Usted también podría obtener la información del navegador del usuario. una solución simple sería añadir el siguiente código en el app_model.example.Esto le dice a Cake qué localización debe usar (si usa una localización regional como fr_FR. como alternativa en caso que no exista.po cada vez que ejecute la tarea i18n de la consola. $value = true) { return parent::invalidate($field. Para evitar la necesidad de editar los archivos default. puede ser utilizando subdominios específicos para cada idioma. lo que significa que tendrá que añadir las entradas a su archivo po manualmente (o a través de su propio script). 9 Paginación Uno de los obstáculos principales al crear aplicaciones web flexibles y amigables al usuario (user-friendly) es diseñar una Interfaz de Usuario intuitiva.Hay otro aspecto de la localización de su aplicación que no está cubierto por el uso de las funciones de traducción.title' => 'asc' ) ). } También puedes incluir otras opciones para find(). 'order' => array( 'Post. Si pasa una localización que no existe en su computadora a setlocale. empaqueta algunas características de ordenación muy fáciles de usar. y el rendimiento y la satisfacción del usuario pueden sufrir. estos son los formatos de fecha y moneda.1 Preparación del controlador En el controlador comenzamos definiendo los valores de paginación por defecto en la variable $paginate. no tendrá ningún efecto. El ayudante PaginatorHelper ofrece una genial solución porque es fácil de usar. como fields class RecipesController extends AppController { var $paginate = array( . Refactorizar lleva tiempo. Puede encontrar la lista de localizaciones disponibles ejecutando el comando $locale -a 4.9. por lo tanto para establecer los formatos para este tipo de cosas deberá utilizar setlocale. Además de paginación. Es importante señalar que la clave 'order' debe estar definida en la estructura de array dada. Por último. 4. Muchas aplicaciones tienden a crecer en tamaño y complejidad rápidamente. CakePHP aligera la carga del desarrollador proveyendo una manera rápida y fácil de paginar los datos. No olvide que CakePHP es PHP :). class RecipesController extends AppController { var $paginate = array( 'limit' => 25. y tanto diseñadores como programadores se encuentran conque no pueden arreglárselas para visualizar cientos o miles de registros. pero no menos importante. Visualizar un número razonable de registros por página ha sido siempre una parte crítica de toda aplicación y solía causar muchos dolores de cabeza a los desarrolladores. también están soportados el paginado y la ordenación Ajax. page. podemos llamar al método paginate() en las acciones del controlador. Este método devuelve los resultados de find('all') del modelo (aplicándoles los parámetros de la paginación). order. 'order' => array( 'Post. } Puedes filtrar los registros pasando condiciones como segundo parámetro al método paginate() $data = $this->paginate('Recipe'.'fields' => array('Post. Este método también añade PaginatorHelper a la lista de helpers en tu controlador.title' => 'asc' ) } ). que son pasadas a la Vista de forma invisible.) ). simplemente nombra cada parte del array según el modelo que desees configurar: class RecipesController extends AppController { var $paginate = array( 'Recipe' => array (.. si es que no estaba ya. array('Recipe. . 'Author' => array (. contain y recursive. } Una vez que la variable $paginate ha sido definida. fields.. function list_recipes() { // similar to findAll(). Pueden incluirse otras claves en el array $paginate similares a los parámetos del método Model>find('all'). 'contain' => array('Article') ). limit. puedes definir más de un conjunto de valores de paginación por defecto en el controllador.title LIKE' => 'a%')). y obtiene algunas estadísticas de paginación adicionales.id'.created'). } Ejemplo de sintaxis usando Containable Behavior: class RecipesController extends AppController { var $paginate = array( 'limit' => 25. 'limit' => 25.. De hecho..). O también puedes ajustar la clave conditions en la variable paginate. $this->set(compact('data')). but fetches paged results $data = $this->paginate('Recipe'). esto es: conditons. 'Post. 4. También es posible ordenar una columna en base a asociaciones: <table> <tr> <th><?php echo $paginator->sort('Title'.ctp <table> <tr> <th><?php echo $paginator->sort('ID'. ?> </table> . Como ya se ha dicho.2 Pagination in Views Es cosa tuya decidir cómo mostrar los registros al usuario. ?> </td> <td><?php echo $recipe['Author']['name']. 'id').9. ?> </td> </tr> <?php endforeach. aunque lo más habitual es hacerlo mediante tablas HTML. pero el PaginatorHelper. 'title'). ?> </table> Los enlaces generados por el método sort() de PaginatorHelper permiten a los usuarios hacer click en las cabeceras de las tablas y alternar la ordenación de los datos por un campo dado. ?> </td> <td><?php echo $recipe['Recipe']['title']. ?></th> <th><?php echo $paginator->sort('Author'.name'). 'Author. ?></th> </tr> <?php foreach($data as $recipe): ?> <tr> <td><?php echo $recipe['Recipe']['id']. Los ejemplos que siguen asumen una disposición tabular. no siempre necesita restringirse de ese modo. ?> </td> </tr> <?php endforeach. ?></th> </tr> <?php foreach($data as $recipe): ?> <tr> <td><?php echo $recipe['Recipe']['title']. 'title'). disponible en las vistas. PaginatorHelper ofrece capacidades para ordenación que pueden integrarse fácilmente en las cabeceras de las columnas de tus tablas: // app/views/recipes/list_recipes. ?></th> <th><?php echo $paginator->sort('Title'. No olvides añadir el componente RequestHandler para poder usar llamadas Ajax en tu controlador: . showing %current% records out of %count% total. array('class' => El texto generado por el método counter() puede personalizarse usando marcadores especiales: <?php echo $paginator->counter(array( 'format' => 'Page %page% of %pages%. donde X es la página actual e Y el total del páginas --> <?php echo $paginator->counter(). añade lo siguiente a tu vista: $paginator->options(array('url' => $this->passedArgs)). ?> Para pasar todos los argumentos de la URL a las funciones del paginador. ajustar el indicador (el icono de carga dentro la DIV) y especificar la DIV que será actualizada en lugar de recargar la página. null. ending on %end %' )).9. El único código extra que necesitas es incluir la librería JavaScript Prototype.Muestra los enlaces para Anterior y Siguiente --> <?php echo $paginator->prev('« Previous '. ?> $paginator->next(' Next »'. starting on record %start%. null. echo 'disabled')). <!-. null.3 Paginación AJAX Es muy fácil incorporar funcionalidad Ajax en la paginación. ?> <!-. "1"))). que también viene proporcionada por PaginationHelper. array('class' => 'disabled')).Muestra X de Y. También puedes especificar qué parámetros pasar manualmente: $paginator->options(array('url' => array("0". 4.El ingrediente final de la paginación en las vistas es añadir la navegación de páginas. ?> <!-. null.Muestra los números de página --> <?php echo $paginator->numbers(). The paginate() method uses the same parameters as Model::find(). $this->set('posts'. 'home_team_id'). $page = 1. After running paginate() do the following. However. You can still use the CakePHP pagination by adding the custom query to the model. if you don't want that and want to use the AjaxHelper or a custom helper for ajax links. $group = $fields = array('week'. } week. $extra = array()) { $recursive = -1. 'page'. you can do so by changing the $helpers array in your controller. $fields. This is true of IBM's DB2. # Configuring the PaginatorHelper to use a custom helper By default in 1. 'fields'. You could also set the 'ajax' key to be any helper. 'limit'. Before continuing check you can't achieve your goal with the core model methods. Will change the PaginatorHelper to use the AjaxHelper for ajax operations. $this->helpers['Paginator'] = array('ajax' => 'Ajax').3 the PaginatorHelper uses JsHelper to do ajax features.group by */ function paginate($conditions. Should you need to create custom queries to generate the data you want to paginate.9. $this->paginate()).4 Custom Query Pagination Fix me: Please add an example where overriding paginate is justified A good example of when you would need this is if the underlying DB does not support the SQL LIMIT syntax. $recursive = 'away_team_id'. To use your own method/logic override it in the model you wish to get the data from. . 'group')).var $components = array('RequestHandler'). 'order'. null. as long as that class implements a link() method that behaves like HtmlHelper::link() 4. 'recursive'. you can override the paginate() and paginateCount() model methods used by the pagination controller logic. return $this->find('all'. away_team_id and home_team_id $order. /** * Overridden paginate method . compact('conditions'. $limit. so please adjust accordingly depending on what database you are using. $extra = array()) { $sql = "SELECT DISTINCT ON(week. this method expects the same arguments as Model::find('count'). /** * Add GROUP BY clause */ var $paginate = array( 'MyModel' => array('limit' => 20. Providing easy. away_team_id FROM games". home_team_id. /** * Or on-the-fly from within the action */ function index() { $this->paginate = array( 'MyModel' => array('limit' => 20. 'group' => array('week'. The example below uses some Postgres-specifc features. } The observant reader will have noticed that the paginate method we've defined wasn't actually necessary . 'home_team_id'. away_team_id) week. 'order' => array('week' => 'desc').10 REST Many newer application programmers are realizing the need to open their core functionality to a greater audience. $recursive = 0. 'group' => array('week'. $results = $this->query($sql).All you have to do is add the keyword in controller's $paginate class variable. unfettered access to your core API can help get your platform accepted. 'away_team_id')) ). and allows for mashups and easy integration with other systems. 'away_team_id')) ). return count($results). While other solutions exist. 'order' => array('week' => 'desc'). REST is a great way to provide easy access to the logic you've .You also need to override the core paginateCount(). However. 'home_team_id'. home_team_id. 4. it will still be necessary to override the paginateCount() method to get an accurate value. /** * Overridden paginateCount method */ function paginateCount($conditions = null. $this->recursive = $recursive. The X_HTTP_METHOD_OVERRIDE 3. These routes are HTTP Request Method sensitive. A basic controller might look something like this: // controllers/recipes_controller.method RecipesController::delete(123) POST /recipes/123. Just set the value of _method to the name of the HTTP request method you wish to emulate. found in app/config. The REQUEST_METHOD header The _method POST variable is helpful in using a browser as a REST client (or anything else that can do POST easily). It's simple. usually XML-based (we're talking simple XML. Once the router has been set up to map REST requests to certain controller actions..php.method RecipesController::edit(123) DELETE /recipes/123.method RecipesController::edit(123) CakePHP's Router class uses a number of different indicators to detect the HTTP method being used.. Exposing an API via REST in CakePHP is simple.10.g.method RecipesController::view(123) POST /recipes. The first line sets up a number of default routes for easy REST access where method specifies the desired result format (e. 4. The Router object features a method called mapResources(). nothing like a SOAP envelope).method RecipesController::index() GET /recipes/123. we can move on to creating the logic in our controller actions. HTTP Method URL.1 The Simple Setup The fastest way to get up and running with REST is to add a few lines to your routes. and depends on HTTP headers for direction.php file.php . that is used to set up a number of default routes for REST access to your controllers. xml.method RecipesController::add() PUT /recipes/123. json.method Controller action invoked GET /recipes. we'd do something like this: //In app/config/routes. Router::mapResources('recipes'). Router::parseExtensions(). rss). If we wanted to allow REST access to a recipe database. Here they are in order of preference: 1.created in your application. The _method POST variable 2. Here's what our index view might look like: // app/views/recipes/xml/index.ctp <recipes> <?php echo $xml->serialize($recipes). } function edit($id) { $this->Recipe->id = $id. the CakePHP router is already primed to serve up different views based on different kinds of requests. } $this->set(compact("message")). Since we're using XML as the content type. the XmlHelper is automatically loaded up for our use in those views. $this->set(compact('recipes')). $this->set(compact('recipe')). We place the REST views for our RecipesController inside app/views/xml. This is on purpose . if ($this->Recipe->save($this->data)) { $message = 'Saved'. . } $this->set(compact("message")). } } Since we've added a call to Router::parseExtensions(). ?> </recipes> Experienced CakePHP users might notice that we haven't included the XmlHelper in our RecipesController $helpers array. the view type is XML. } function view($id) { $recipe = $this->Recipe->findById($id). } else { $message = 'Error'. } else { $message = 'Error'.class RecipesController extends AppController { var $components = array('RequestHandler').when serving up a specific content type using parseExtensions(). } function delete($id) { if($this->Recipe->delete($id)) { $message = 'Deleted'. function index() { $recipes = $this->Recipe->find('all'). We can also use the XmlHelper for quick-and-easy XML output in those views. Since we're dealing with REST requests. CakePHP automatically looks for a view helper that matches the type. array("action" => "edit". array("id" => "[0-9]+") ) Advanced routing techniques are covered elsewhere."></comment> </post> </posts> Creating the logic for the edit action is a bit trickier. "[method]" => "PUT"). 4. so we'll focus on the most important point for our purposes here: the [method] key of the options array in the second parameter.The rendered XML will end up looking something like this: <posts> <post id="234" created="2008-06-13" modified="2008-06-14"> <author id="23423" first_name="Billy" last_name="Bob"></author> <comment id="245" body="This is a comment for this post. it's a natural choice to receive XML as input. etc. The connect() method allows you to define a number of different options for a given URL. The third parameter allows you to specify regex patterns to help CakePHP identify certain markers in the specified URL. Everything you need should end up in $this->data. Here's what our edit REST route would look like. without using mapResources(): Router::connect( "/:controller/:id". handling XML and POST data in parallel is seamless: no changes are required to the controller or model code. the specified route works only for that HTTP request method (which could also be GET. however: the RequestHandler and Router classes make things much easier. but not by much. and the second parameter allows you to supply those options.10. If a POST or PUT request has an XML content-type. Once that key has been set. Because of this feature. use the Router::connect() method to define a custom set of REST routes. Not to worry. which is assigned to the $data property of the controller. DELETE.2 Custom REST Routing If the default routes created by mapResources() don't work for you.) . Since you're providing an API that outputs XML."></comment> </post> <post id="3247" created="2008-06-15" modified="2008-06-15"> <author id="625" first_name="Nate" last_name="Johnson"></author> <comment id="654" body="This is a comment for this post. and allow you to tailor this route for your other RESTful purposes. then the input is taken and passed to an instance of Cake's Xml object. The first parameter is the URL itself. We'll provide a simple example here. El componente Auth provee un sistema de autenticación fácil de utilizar usando diferentes procesos de validación. las listas de control de acceso son una herramienta extremadamente poderosa para tener a mano al desarrollar tu aplicación. incluso cuando las cosas se compliquen. Éstos proveen distintas funcionalidades para tareas realizadas comúnmente.1 Listas de Control de Acceso La funcionalidad de listas de control de acceso en CakePHP es una de las más comentadas. o aprende acerca de cómo crear tus propios componentes.5 Componentes del Núcleo CakePHP posee una serie de componentes integrados. El componente Cookie se comporta en cierta forma similar al Session ya que provee Auth Session RequestHandler informarle a la aplicación acerca del tipo de contenido y la información requerida Security Email Cookie un wrapper para el soporte nativo de cookies en PHP. y en parte porque puede ser algo confusa al principio. El componente Session provee un wrapper de almacenamiento independiente a las sesiones de PHP. El componente Security permite aumentar la seguridad y gestionar autenticación HTTP. continúa leyendo. 5. . como ser callbacks en los controladores. Una vez que asimiles estos conceptos. El componente RequestHandler permite analizar las peticiones HTTP para por el usuario. Acl El componente Acl provee una sencilla interfaz para listas de control de acceso (access control list) basadas en archivos ini o base de datos. Para aprender más acerca de cada componente mira en el menu a la izquierda. Una interfaz que puede ser utilizada para enviar emails usando distintos MTA (mail transfer agent) incluyendo la función mail() de PHP y el protocolo SMTP. Acl u callbacks en los objetos. Debes ser valiente. en parte porque es una de las más solicitadas. Si estás buscando un buen lugar para comenzar a utilizar ACL en general. que la mayoría de las veces son los usuarios.5. Imaginemos que el grupo de aventureros de la novela de fantasía El señor de los Anillos trabaja con una aplicación CakePHP.Objeto que se quiere controlar • ARO . son los ARO (en inglés access request objects).Access Request Object . Vamos a utilizar un ejemplo práctico para ver cómo encajan todas estas piezas. son los ACO (en inglés access control objects).1 Entendiendo cómo funciona ACL Las listas de control de acceso permiten gestionar detalladamente los permisos de una aplicación de forma sencilla y escalable. y que el líder. y las entidades del sistema que se quiere controlar. lo primero que hace Gandalf es crear la lista de AROs involucrados: • Gandalf • Aragorn • Bilbo • Frodo • Gollum • Legolas • Gimli • Pippin • Merry . Para garantizar la privacidad y la seguridad de los miembros del grupo. hasta el diario en línea íntimo de tu abuela.1. que normalmente son acciones o datos. o ACL. En resumen: • ACO . las ACL se utilizan para decidir cuándo un ARO puede acceder a un ACO. A los ARO se les llama 'objetos' porque quien realiza la petición no siempre es una persona. las entidades que quieren controlar algo. se encarga de gestionar los elementos del grupo. manejan principalmente dos cosas: las entidades que solicitan el control de algo y las entidades que se quiere controlar. los ACO son cualquier cosa que desees controlar: desde la acción de un controlador o un servicio Web. Las listas de control de acceso. Gandalf.Access Control Object . pues.Objeto que solicita el control de algo Esencialmente. En la jerga de ACL. Ciertamente. en este ejemplo. es difícil mantenerla. en un sistema que tenga previsto crecer. A continuación. ACL es lo que ocurre después de que el usuario se autentica. gestionándolos por unidad? Otro inconveniente de las matrices es que no permiten formar grupos lógicos de usuarios ni aplicar cambios de permiso en cascada a estos grupos. las asignaciones garantizan la seguridad (sólo Frodo puede acceder al anillo) y previenen los accidentes (los Hobbits se mantienen lejos del jamón y de las armas). o ACOs. una vez terminada la batalla. que el sistema maneja. es importante ver la diferencia entre saber quién es alguien (autenticación) y saber qué puede hacer (ACL). parece que este tipo de sistema funciona bastante bien. En efecto. los hobbits pudieran acceder a la cerveza y al . Esta lista puede ser como la siguiente: • Armas • El Anillo • Jamón • Diplomacia • Cerveza Armas El Anillo Jamón Diplomacia Cerveza Gandalf Aragorn Permitir Bilbo Frodo Gollum Legolas Gimli Pippin Merry Permitir Permitir Permitir Permitir Permitir Permitir Permitir Permitir Permitir Permitir Permitir Permitir Permitir Permitir Permitir Permitir Permitir Permitir Permitir A simple vista. Parece bastante detallado y fácil de leer. cómo se controlaría el acceso a cientos de campamentos de guerra. Gandalf tiene que crear una lista con las cosas. estaría muy bien que. ¿verdad? Sin embargo. ¿te imaginas. o que tenga muchos recursos (ACOs) y usuarios (AROs). una matriz como esta sólo funciona en un sistema pequeño. Aunque suelen utilizarse los dos a la vez. En otras palabras.Fíjate que ACL no es lo mismo que la autenticación. generalmente. Jamón) • Aragorn • Legolas • Gimli • Magos .jamón. ACL se suele implementar en una estructura de árbol. Organizando los objetos así. Cerveza. Gandalf añade ahora estos permisos a los grupos: • Comunidad del Anillo (Denegar: todo) • Guerreros (Permitir: Armas. Gandalf decide utilizar ACL en su sistema y organiza los objetos de la siguiente manera: • Comunidad del Anillo™ • Guerreros • Aragorn • Legolas • Gimli • Magos • Gandalf • Hobbits • Frodo • Bilbo • Merry • Pippin • Visitantes • Gollum Utilizar una estructura de árbol en los AROs permite a Gandalf definir permisos que se aplican a un grupo de usuarios. existe un árbol de AROs y un árbol de ACOs. Raciones. en otras palabras. otorgar individualmente permisos a hobbits es tedioso y propenso a errores. siendo el sabio líder que es. los permisos se gestionan de forma granular y se mantiene una visión general. Por lo tanto. de una sola vez. y. en consecuencia. mientras que aplicarles un cambio en cascada es mucho más fácil. el resultado final es que se permite el acceso a ese ACO. Acceso a la Cerveza permitido! Todavía se le permite la Cerveza! Como el nodo de 'Pippin' del árbol ACL no deniega específicamente el acceso al ACO 'Cerveza'. lo primero que tenemos que hacer es obtener su camino en el árbol: Comunidad del Anillo->Hobbits->Pippin. Diplomacia. Raciones. Cerveza. el árbol permite realizar ajustes más finos para tener un control más granular y no pierda la capacidad de realizar cambios importantes en los grupos de AROs: • Comunidad del Anillo (Denegar: todo) • Guerreros (Permitir: Armas. hay que ver los permisos que residen en cada uno de esos puntos. finalmente. Nodo ARO Comunidad del Anillo Hobbits Pippin Información de Permisos Denegar todo Permitir 'Cerveza' -Resultado Acceso a la Cerveza denegado. Jamón) • Aragorn (Permitir: Diplomacia) • Legolas . se utiliza el más específico relacionado con Pippin y la Cerveza. Cerveza) • Gandalf • Hobbits (Permitir: Cerveza) • Frodo • Bilbo • Merry • Pippin • Visitantes (Permitir: Jamón) • Gollum Para ver si Pippin tiene acceso a la cerveza. y. Además. A continuación.(Permitir: Jamón. podemos decir que. Por lo tanto. Cerveza) • Gandalf • Hobbits (Permitir: Cerveza) • Frodo (Permitir: Anillo) • Bilbo • Merry (Denegar: Cerveza) • Pippin (Permitir: Diplomacia) • Visitantes (Permitir: Jamón) • Gollum Esta aproximación permite realizar cambios de permisos globales y. Acceso a la Cerveza permitido! Cerveza denegada. al mismo tiempo. a excepción de Merry. Diplomacia. primero debemos encontrar su camino en el árbol. Comunidad del Anillo->Hobbits->Merry. todos los hobbits tienen acceso a la cerveza. De nuevo. bajar. ajustes granulares.• Gimli • MAgos (Permitir: Jamón. para saber si Merry tiene acceso a la Cerveza. . y analizar los premisos relacionados con la Cerveza: Nodo ARO Comunidad del Anillo Hobbits Merry Información de Permisos Denegar todo Permitir 'Cerveza' Denegar 'Cerveza' Resultado Acceso a la Cerveza denegado. 2 Definiendo Permisos: ACL basado en INI La primer implementación de ACL en Cake fue basada en archivos INI almacenados en el directorio de instalación de Cake. permitir y denegar.database'. Los permisos ARO/ACO son especificados en /app/config/acl. //Para que se vean como éstas: Configure::write('Acl.ini. Si bien es útil y estable. La implementación con archivos INI fue pensada para ser utilizada en aplicaciones simples. 'IniAcl'). 'default'). 'DbAcl'). . • denegar: nombres de los ACOs a los que este ARO no tiene acceso. 'default'). Por defecto. sobre todo por la posibilidad de crear nuevos ACOs y AROs dentro de la aplicación.classname'. recomendamos que utilices la solución de ACL apoyada en la base de datos. • permitir: nombres de los ACOs a los que tiene acceso este ARO. • grupos: nombres de los grupos de AROs a los que pertenece este ARO. tiene que decirle a CakePHP qué sistema vas a usar editando las siguientes líneas en app/config/core. Básicamente los AROs se deben especificar en la sección del INI que tiene tres propiedades: grupos. Para habilitar el uso de ACL con archivos INI.php.classname'.php //Cambiar éstas líneas: Configure::write('Acl. especialmente en aquellas que por alguna razón podrían no utilizar una base de datos. //Configure::write('Acl.database'.5.1. CakePHP utiliza el sistema de ACL apoyado en la base de datos. Configure::write('Acl. ARO Groups . jamón [magos] permitir = jamón. Como ejemplo.------------------------------------[guerreros] permitir = armas.------------------------------------. veamos cómo se vería la estructura de AROs de la Comunidad del Anillo en la sintaxis del INI: .------------------------------------[aragorn] grupos = guerreros permitir = diplomacia [legolas] grupos = guerreros [gimli] grupos = guerreros [gandalf] grupos = magos [frodo] grupos = hobbits permitir = ring [bilbo] grupos = hobbits [merry] grupos = hobbits deny = cerveza [pippin] grupos = hobbits [gollum] grupos = visitantes . cerveza.------------------------------------.Los ACOs se definen en la sección del INI que sólo incluye las propiedades permitir y denegar. diplomacia. puede saltar a la sección de verificación de permisos utilizando el componente ACL. cerveza [hobbits] permitir = cerveza [visitantes] permitir = jamón Ahora que ya has definido los permisos. . AROs . 1. Los modelos son usados por Cake para interactuar con la base de datos para poder almacenar y recuperar los nodos en forma de árbol.1 para más información acerca de la configuración de la base de datos. La aplicación de consola se utiliza para inicializar la base de datos e interacturar con los árboles de ACOs y AROs Para comenzar. La salida de la consola debería verse algo así: --------------------------------------------------------------Cake Schema Shell --------------------------------------------------------------The following tables will be dropped.3. este comando las elimina y las vuelve a crear). tienes que utilizar la consola de CakePHP para crear las tablas de ACL: $ cake schema run create DbAcl Ejecutando este comando recreará las tablas necesarias para almacenar los árbols de ACOs y AROs (si las tablas ya existían.php exista y esté correctamente configurado. lo primero que deber asegurarte es que /app/config/database. acos aros aros_acos Are you sure you want to drop the tables? (y/n) [n] > y Dropping tables. acos aros aros_acos . Una vez que hayas hecho esto.1. aros_acos updated.5. Referirse a la sección 4.1 Comenzando La implementación de ACL por defecto está basada en la base de datos. 5. The following tables will be created. acos updated. aros updated. Ésta consiste en un conjunto de modelos y una aplicación de consola que viene con la instalación de Cake.3 Definiendo Permisos: ACL en la base de datos Ahora que ya hemos cubierto los permisos de ACL basados en archivos INI. veamos los permisos (más comúnmente utilizados) basados en base de datos. y parent_id. cuando estás creando un grupo o un nivel más alto de objetos. deber usar un alias. También puedes utilizar el archivo SQL que se encuentra en app/config/sql/db_acl. El componente ACL utiliza el Comportamiento de Árbol para gestionar la herencia dentro del árbol. El primer método consiste en realizar un enlace entre el objeto ACL directamente con el registro de la base de datos. en los cuales existen algunos campos que necesitas conocer para almacenar la información: model.Are you sure you want to create the tables? (y/n) [y] > y Creating tables. pero está lejos de ser tan divertido. alias. Los campos model y foreign_key de un objeto ACL te permiten enlazar directamente el objeto con el correspondiente registro de la base de datos (si existe alguno). Cuando finalices. En general. El segundo método puede usarse cuando un objeto no tiene relación con un registro en la base de datos. creemos algunos árboles de AROs y ACOs 5. aros updated. hay dos formas de nombrar y acceder a los nodos. muchos . Las clases pertenecientes a los modelos de ACL están contenidas en un único archivo db_acl. especificando el nombre del modelo y el valor de la clave externa.2 Creando Access Request Objects (AROs) y Access Control Objects (ACOs) Al crear nuevos objetos ACL (ACOs y AROs). Si eres curioso en saber cómo Cake almacena la información de los árboles en esas tablas. Por ejemplo. Si estás gestionando el acceso a un registro específico de la base de datos. debes usar el método de modelo/clave externa. Ésto reemplaza al viejo comando "initdb".sql. End create. Ahora que ya hemos configurado todo.1.php. Para crear nuevos objetos ACL debes utilizar los modelos de ACL provistos por CakePHP. deberías tener tres nuevas tablas en tu sistema: acos. aros_acos updated.3. el cuál ya es obsoleto. puedes proveer un alias para este tipo de objetos. aros. acos updated. and aros_acos (la tabla de la relación n a n donde se definen los permisos entre los dos árboles). foreign_key. debes leer acerca del recorrido de árboles en bases de datos (en inglés modified database tree traversal). podrías elegir enlazar un ACO a ese registro en particular. creemos primero nuestro grupo de ACOs. Estableciendo el campo foreign_key del ARO con el ID del Usuario te permitirá enlazar la información del ARO y del Usuario con una simple llamada find() del modelo del Usuario si las relaciones fueron configuradas correctamente. usaremos alias en la creación de los objetos ACL. veamos ejemplos de cómo sería la creación de algunos objetos. si lo que quieres es gestionar las operaciones de editar en un post específico en un blog o un listado de recetas. Antes de crear nuevos objetos ACL. En cambio. Una vez que haz hecho esto. sólo estamos utilizando los modelos para guardar los datos como siempre hacemos: . Siguiendo con la configuración de la Comunidad. pero deberías sentirte cómodo usando estas técnicas para crear AROs y ACOs al vuelo. Los alias son muy útiles para nombrar grupos de usuarios en colecciones de ACOs. El campo parent_id de un objeto ACL te permite completar la estructura del árbol. El código siguiente puede ser colocado en la acción de algún controlador: Mientras los ejemplos se enfocan en la creación de AROs. las mismas técnicas pueden ser usadas para crear el árbol de ACOs. Lo que estamos haciendo aquí es desde la perspectiva de la acción de un controlador. La forma más fácil de hacer esto es incluir el componente ACL en el array $components de tu controlador: var $components = array('Acl'). y se utiliza para identificar un objeto ACL que no tiene una correlación directa con algún registro de un modelo. Lo que vamos a usar es una aproximación algo artificial. Debes proveer el ID del nodo padre en el árbol para crear un nuevo hijo. Debido a que nuestros grupos no tendrán registros específicos asociados a ellos. pero puede realizarse en otro lugar. necesitamos cargar las respectivas clases.AROs tendrán su correspondencia con registros de Usuarios en la base de datos. El campo alias de un objeto ACL es sólo una etiqueta que puede ser fácilmente interpretada por un ser humano. Esto no debería ser algo drásticamente nuevo. podemos utilizar la aplicación de consola de ACL para verificar la estructura de los árboles. $aro->create().. Agreguemos algunos hijos a esos nodos agregando nuestros AROs específicos de cada usuario dentro de esos grupos. Todo buen ciudadano de la Tierra Media tiene . Una vez creados. } } //Aquí va otra lógica de la acción.function algunaAccion() { $aro = new Aro().. pero al menos pudimos verificar que tenemos los cuatro nodos de primer nivel. 2 => array( 'alias' => 'hobbits' )... ). 3 => array( 'alias' => 'visitantes' ). $ cake acl view aro Aro tree: --------------------------------------------------------------[1]guerreros [2]magos [3]hobbits [4]visitantes --------------------------------------------------------------- Supongo que no se parece mucho a un árbol en este punto. //Aquí tenemos la información de nuestros grupos en un array sobre el cual iteraremos luego $groups = array( 0 => array( 'alias' => 'guerreros' ). //Iterar para crear los ARO de los grupos foreach($groups as $data) { //Recuerda llamar a create() cuando estés guardando información dentro de bucles. //Guardar datos $aro->save($data). 1 => array( 'alias' => 'magos' ). 1 => array( 'alias' => 'Legolas'. . 'model' => 'User'. ). $users = array( 0 => array( 'alias' => 'Aragorn'.una cuenta en nuestro nuevo sistema. asegúrese de utilizar el ID del nodo ACL y no un valor de foreign_key. 'foreign_key' => 2356. listos para ser relacionados con nuevos registros ARO //Estos datos pueden venir de un modelo. ). 'parent_id' => 1. 'model' => 'User'. 'model' => 'User'. Cuando agregue hijos al árbol. 'parent_id' => 1. 2 => array( 'alias' => 'Gimli'. 'foreign_key' => 1564. //Aquí tenemos nuestros registros de usuario. 'model' => 'User'. pero en este caso estamos usando arrays estáticos //con propósitos de demostración. 'foreign_key' => 7419. 'parent_id' => 2. entonces nosotros referiremos esos AROs a los registros dentro del modelo específico en la base de datos. 3 => array( 'alias' => 'Gandalf'. 'foreign_key' => 6342. ). 'parent_id' => 1. function algunaAccion() { $aro = new Aro(). 'model' => 'User'. 7 => array( 'alias' => 'Pippin'.). 'foreign_key' => 5144. 'model' => 'User'. 'foreign_key' => 7451.. 'parent_id' => 3. 'foreign_key' => 5126. $aro->create(). 'parent_id' => 3. ). 'foreign_key' => 1337. ). 6 => array( 'alias' => 'Merry'. 'parent_id' => 3. 'model' => 'User'. 'parent_id' => 4. 'model' => 'User'. 4 => array( 'alias' => 'Frodo'. 'foreign_key' => 1211. 8 => array( 'alias' => 'Gollum'. ). //Iterar y crear los AROs (como hijos) foreach($users as $data) { ///Recuerda llamar a create() cuando estés guardando información dentro de bucles. 5 => array( 'alias' => 'Bilbo'. 'parent_id' => 3. 'model' => 'User'.. ). . ). ). La salida de la aplicación de consola ahora debería ser un poco más interesante. a menudo es más práctico modelar un árbol de ACOs . } //Aquí va otra lógica de la acción..//Guardar datos $aro->save($data). } Típicamente no usarás el alias y los campos model/foreing_key al mismo tiempo. Mientras que podemos estructurar más de una representación abstracta de nuestros ACOs.. discutamos una posible aproximación para la estructura del árbol de ACOs. Veamos: $ cake acl view aro Aro tree: --------------------------------------------------------------[1]guerreros [5]Aragorn [6]Legolas [7]Gimli [2]magos [8]Gandalf [3]hobbits [9]Frodo [10]Bilbo [11]Merry [12]Pippin [4]visitantes [13]Gollum --------------------------------------------------------------- Ahora que ya tenemos nuestro árbol de AROs configurado apropiadamente. pero aquí estamos utilizando los dos para que la estructura sea más fácil de leer y para propósitos de demostración. update.3. pero usando la gestión de acciones que provee Cake abarcarás las operaciones básicas de CRUD sobre un objeto dado. read. Como ya eres un profesional creando AROs. es que cada ACO automáticamente contiene cuatro propiedades relacionadas con las acciones CRUD (en inglés create. finalmente podremos asignar los permisos entre los dos grupos. Puedes crear nodos dentro de cada uno de esos ACOs principales. Tenemos cindo objetos principales que queremos manejar en este escenario. usa las mismas técnicas para crear este árbol de ACOs. Esto se realiza utilizando el componente ACL de Cake. Como tenenos cinco ACOs. vamos a crear un árbol de ACOs que imite una aplicación Cake. Sigamos con nuestro ejemplo En este ejemplo trabajaremos en el contexto de una acción de un controlador. y la configuración natural para una aplicación Cake es un grupo de modelos y en segundo lugar. Veremos cómo usadas esas propiedades cuando hablemos acerca de cómo asignar permisos.1. Tenemos que hacer esto debido a que los permisos son manejados por el componente ACL class AlgunController extends AppController { // Podrías colocar esta declaración en AppController // para que sea heredada por todos los controladores var $components = array('Acl'). Es en éstos controladores donde queremos controlar el acceso a algunas acciones específicas. and delete).3 Asignando Permisos Después de crear los ACOs y los AROs. Crea esos grupos de nivel superior usando el modelo básico de Aco 5. los controladores que los manipulan. vamos a crear un árbol de ACOs que se verá algo así: • Armas • Anillo • Jamón • EsfuerzosDiplomáticos • Cervezas Una buena característica de la implementación de ACL en Cake.después de configurar los controladores y acciones. Basándonos en esa idea. } . Teniendo esto en mente hará que tu árbol de ACOs sea más pequeño y fácil de mantener. $this->Acl->deny('guerreros/Gimli'. read. => 'User'. } 'Armas'. 'Armas'). pero se puede usar también de la forma model/foreign_key. . $this->Acl->deny(array('model' 'delete'). die(print_r('hecho'. En la primer llamada que hicimos al componente ACL permitimos que cualquier usuario dentro del grupo ARO denominado "guerreros" tenga acceso irrestricto a cualquier arma dentro del grupo ACO denominado "Armas". pero no queremos que los otros guerreros pertenecientes al grupo tengan la habilidad de borrar registros de Armas. Esto lo realizamos referenciando a ambos grupos por sus alias. La segunda llamada es un intento de realizar una asignación de permisos mucho más granular. por ejemplo _admin) y utilizarla junto con las otras acciones. La opción por defecto para ese parámetro son create. => 'User'. update. 'delete'). El ejemplo anterior es equivalente a este: // 6342 = Legolas // 1564 = Gimli $this->Acl->deny(array('model' 'delete'). 'delete').Asignemos algunos permisos básicos utilizando el componente ACL in la acción dentro de este controlador function index() { //Permitirle a los guerreros acceso total a las armas //Ambos ejemplos usan la sintaxis de alias vista anteriormente $this->Acl->allow('guerreros'. Nosotros queremos que Aragorn tenga acceso irrestricto. pero puedes agregar una columna en la tabla aros_acos (comenzando con el prefijo "_". 'foreign_key' => 1564). 'foreign_key' => 6342). 'Armas'. 1)). 'Armas'. y delete. 'Armas'. //Aunque el Rey puede no querer que todo el mundo tenga //acceso irrestricto $this->Acl->deny('guerreros/Legolas'. En el ejemplo utilizamos la sintaxis de alias. ¿Has notado el uso del tercer parámetro? Ahí es donde utilizamos esas acciones pre-contruidos para todos los ACOs dentro de Cake. 'update'). //Estos también deben devolver true: $result = $this->Acl->check('guerreros/Legolas'. La próxima sección nos ayudará a validar nuestra configuración utilizando el componente ACL para verificar los permisos que acabamos de asignar. 'foreign_key' => 8282). 'create'). 'Armas'). pero esperamos que puedas ver cómo la verificación de permisos de esta forma puede ser utilizada para decidir cuándo permitir o no determinada acción. 'delete'). mostrar un mensaje de error o redirigir al usuario a la pantalla de autenticación. Vamos a intentarlo dentro de una acción en un controlador: function index() { //Todos estos devuelven true $this->Acl->check('guerreros/Aragorn'. 'Armas'.1. $this->Acl->check('guerreros/Aragorn'. La sintaxis básica para realizar una verificación de permisos es: $this->Acl->check( $aro. Para acceder a un nodo utilizando la sintaxis model/foreign_key debes utilizar un arreglo con dos parámetros: array('model' => 'Usuario'. } El uso que le dimos aquí es solamente con propósitos de demostración. 'foreign_key' => 2356). $action = '*').3. 'create').'delete'). $this->Acl->check('guerreros/Aragorn'. 'Armas'. 'Armas'. . 5.Para acceder a un nodo utilizando la sintaxis de alias. 'delete'). $result = $this->Acl->check('guerreros/Gimli'. $this->Acl->check('guerreros/Aragorn'.4 Verificando Permisos: El Componente ACL Vamos a utilizar el componente ACL para asegurarnos que ni los enanos ni los elfos pueden quitas armas de la armería. 'Armas'. 'Armas'). En este punto. 'read'). 'Armas'. 'Armas'. 'Armas'. 'read'). debemos usar una cadena de caracteres delimitada por barras ('/usuarios/empleados/desarrolladores'). $aco. //Pero estos devuelven false: $result = $this->Acl->check('guerreros/Legolas'. deberíamos ser capaces de utilizar AclComponent para verificar los permisos entre los ACOs y AROs que hemos creado. $result = $this->Acl->check('guerreros/Gimli'. 'Armas'. $this->Acl->check('guerreros/Aragorn'. //Recuerda que también podemos utilizar la sintaxis model/foreign_key //para los AROs de nuestro usuario $this->Acl->check(array('model' => 'User'. se utiliza mediante la incorporación de 'Auth' a la lista de componentes en el controlador: class FooController extends AppController { var $components = array('Auth'). password char(50). O añadelo al AppController si todos tus controladores lo van a usar: class AppController extends Controller { var $components = array('Auth'). El AuthComponent de CakePHP se puede usar para crear un sistema fácil y rápidamente. por ejemplo. el componente da al usuario acceso completo a sitio. Ahora. En algunos casos. En CakePHP hay muchas formas para autenticar usuarios. Vamos a crear nuestra tabla 'users' usando el siguiente SQL: CREATE TABLE users ( id integer auto_increment. PRIMARY KEY (id) ). Veamos como construir un sistema de autenticación simple. La esencia de componente de autenticación es comprobar si el usuario tiene una cuenta con el sitio. Este componente se puede combinar con el componente ACL (access control lists) para crear niveles más complejos de acceso al sitio. podría permitir acceso a un usuario a áreas publicas del sitio. hay unas pocas convenciones en las que pensar cuando se usa el AuthComponent. mas tarde. Por defecto. mientras que concede a otro usuario acceso a porciones administrativas protegidas del sitio.5. Al igual que todos los componentes. . El componente ACL. veremos como cambiar el nombre por defecto de los campos para trabajar con nuestro propio entorno. el AuthComponent espera que se tenga una tabla llamada 'users' con campos llamados 'username' y 'password'. cada una de estas provee diferentes opciones. username char(50). las bases de datos no permiten usar 'password' como nombre de columna.2 Autenticación Un sistema de autenticación de usuarios es una parte común de muchas aplicaciones web. De ser así. var $components = array('Auth').Algo a tener en cuenta a la hora de crear una tabla para almacenar todos los datos de autenticación del usuario es que el AuthComponent espera el valor del password almacenado esté encriptado en vez de estar almacenado en texto plano. //No es necesario si se declaro en el app controller /** * * */ function login() { } function logout() { $this->redirect($this->Auth->logout()). Si bien usted puede dejar la función login() en blanco. } } El AuthComponent proporciona la funcionalidad necesaria para el acceso (login). Asegúrese de que el campo que utilizará para almacenar la contraseña sea suficientemente largo para almacenar el hash (40 caracteres para SHA1. ?> . Para la configuración más básica usted solo tiene que crear dos acciones en el controlador: class UsersController extends AppController { var $name = 'Users'. El siguiente ejemplo asume que ya está familiarizado con el uso del Form helper: <?php echo $session->flash('auth'). sin embargo. echo $form->create('User'. por lo que se puede dejar esta función en blanco. por ejemplo). necesitara crear la vista para la acción login (guardela en app/views/users/login. echo $form->input('password').ctp). echo $form->input('username'). echo $form->end('Login'). array('action' => 'login')). Esta es la única vista del UsersController que es necesario crear. Una vez enviado este formulario. ¡no olvidemos que también hay que cambiar en la vista el nombre del campo! Las variables del componente Auth también se utilizan para que los usuarios que no han entrado en el sistema puedan acceder a determinados métodos. Para cambiar el nombre del campo que se utiliza para guardar las contraseñas. y configurar algunas variables del componente.2.Esta vista crea un simple formulario de login en el cual introducir el nombre de usuario y la clave. a 'secretword'. 5. por ejemplo. Por ejemplo. el AuthComponent se encargará del resto por usted. ya está! Esta es la manera de implementar una increiblemente simple. Lo creas o no. 'password' => 'secretword' ). base de datos de autenticación usando el componente Auth. } } En este caso.1 Configurando las variables del componente Auth Para cambiar las opciones predeterminadas de AuthComponent tienes que crear el método beforeFilter() en el controlador. 'password'. hay mucho más que podemos hacer.'view'). haríamos lo siguiente: class UsersController extends AppController { var $components = array('Auth'). Echemos un vistazo a algunos usos más avanzados del componente. } . Sin embargo. llamar a varios métodos predefinidos. si queremos que todos los usuarios puedan acceder solamente a los métodos index y view. function beforeFilter() { $this->Auth->fields = array( 'username' => 'username'. hacemos lo siguiente: function beforeFilter() { $this->Auth->allow('index'. El session flash message mostrara cualquier información generada por el AuthComponent. 3 Problemas comunes con Auth A veces puede ser difícil diagnosticar problemas cuando encuentras comportamientos inesperados.flash')) { $session->flash(). Password hashing Al enviar información a través de un formulario. el componente Auth encripta automáticamente el contenido del campo contraseña.2. } ?> 5.2. $this->User->save($this->data).5. necesitas añadir el siguiente código en tu vista. asegúrate de que el usuario rellene un campo "confirmar contraseña" que puedas comparar. Recordar estos puntos te puede ayudar. si también hay datos en el campo nombre de usuario. En este caso. Aquí va un código de ejemplo: <?php function register() { if ($this->data) { if ($this->data['User']['password'] == $this->Auth->password($this>data['User']['password_confirm'])) { $this->User->create(). } } } ?> . Así que si estás intentando crear algún tipo de página de registro de nuevo usuario.2 Mostrando Mensajes de Error en la Autenticación Con el objetivo de desplegar los mensajes de error que la autentificación muestra.auth')) { $session->flash('auth'). el mensaje aparecerá debajo de los mensajes flash regulares: <?php if ($session->check('Message. } if ($session->check('Message. . el método hashPasswords() del modelo User será llamado cada vez que Cake llame a AuthComponent::hashPasswords().. } Con el código anterior. La clase Security usa el esquema SHA1 por defecto. Para hacer esto. Para cambiar a otra función hash usada por el componente Auth. } } .5.podrías necesitar hacer esto si.4 Cambiar la Función Hash AuthComponent usa la clase Security para encriptar una contraseña. Aquí está un ejemplo del método hashPasswords. return $data.php) para el hashing de la contraseña. Si quieres usar una lógica diferente para el hashing de la contraseña más allá de md5/sha1 con el valor salt de la aplicacion. crea el metodo hashPasswords en la clase que quieras que se haga a cargo del hashing de las contraseñas (normalmente el modelo User ) y establece el atributo authenticate de Auth al objeto contra el que se está autenticando (normalmente es User) de este modo: function beforeFilter() { $this->Auth->authenticate = ClassRegistry::init('User'). por ejemplo. sha1 o sha256 como primer y único parámetro. } return $data.2. parent::beforeFilter(). necesitará reescribir el mecanismo estandar hashPassword . Security::setHash('md5'). apropiado si ya tienes una tabla de usuarios repleta de contraseñas de hash 'plain md5': class User extends AppModel { function hasPasswords($data) { if (isset($data['User']['password'])) { $data['User']['password'] = md5($data['User']['password']). // o sha1 o sha256. tuvieses una base de datos existente que anteriormente usaba un esquema de hashing sin un valor de salt. usa el método setHash pasándole md5. La clase Security usa el valor de inicialización (salt value.. que se encuentras en /app/config/core. $this->Auth->allow('foo'. $this->Auth->allow('register').2 allow Si tienes acciones en tu controlador que no necesitas que se autentiquen contra ellas (como una acción de registro de usuarios)... function beforeFilter() { . 'bar'. utilizará el par controlador/acción actual (el que se está ejecutando).5.5. $this->Auth->allow('*'). puedes obtener la ruta al nodo del ACO que está enlazado a un par controlador/acción particular.2. function beforeFilter() { . Si no le pasas valores. .. } Si estás usando requestAction en tu layout o en tus elementos.5. deberías permitir esas acciones para poder abrir la página de login correctamente.5 Métodos de AuthComponent 5. puedes agregar métodos que debe ignorar AuthComponent. } Si deseas permitir que múltiples acciones no usen autenticación.. $acoNode = $this->Auth->action('users/delete').2. } Atajo: también puedes permitir todas las acciones en un controlador usando '*'. El siguiente ejemplo muestra como permitir una acción llamada 'register'.2.. 'baz').. las pasas como parámetros al método allow(): function beforeFilter() { . El componente auth supone que tus nombres de acciones siguen las convenciones y usan guiones bajos. 5.1 action action (string $action = ':controller/:action') Si estas usando ACOs como parte de tu estructura ACL. $this->Auth->allow('delete'). $data['User']['password'] = 'changeme'.. /* devuelve: Array ( [User] => Array ( [username] =>
[email protected]'.. Esta función debe ser usada antes de realizar llamadas de inserción o actualización de los datos del usuario cuando afecta al campo contraseña. tal y como está especificado en la variable $fields indexados por el nombre del modelo especificado en $userModel.5. $data['User']['username'] = '
[email protected] deny Habrá algunas veces que quieras eliminar acciones de la lista de acciones permitidas (añadidas usando $this->Auth->allow()).5. $hashedPasswords = $this->Auth->hashPasswords($data). } 5. realiza el hash del campo contraseña en el array y devuelve el array $data con el mismo formato.4 hashPasswords hashPasswords ($data) Este método verifica si $data contiene los campos nombre de usuario(username) y contraseña(password).5. } function isAuthorized() { if ($this->Auth->user('role') != 'admin') { $this->Auth->deny('delete'). pr($hashedPasswords). } .com [password] => 8ed3b7e8ced419a679a7df93eff22fae ) ) */ . Si el array $data contiene el nombre de usuario y la contraseña. He aquí un ejemplo: function beforeFilter() { $this->Auth->authorize = 'controller'.2. 5 mapActions Si estás utilizando Acl en modo CRUD. en una aplicación tal vez desees asignar a un usuario una contraseña y autoidentificarlo en el sistema tras el registro.array('action'=>'registrar')).5. 'read' => array('ciertaAccion'. puedes usar este método para identificar manualmente a alguien en el sistema. Si tu controlador usa el compoente Auth y los datos recibidos por POST contienen los campos explicados arriba. Si no pasas ningún valor para $data. 'delete' => array('ciertaAccion') ) ). Controlador: function registrar() { if(!empty($this->data)) { $this->User->create().2. 'update' => array('ciertaAccion'). echo $form->end('Regístrame'). tal vez desees asignar ciertas acciones no predeterminadas a cada parte de CRUD.5. 5. 5. . automáticamente usará los datos enviados mediante POST al controlador. 'ciertaAccion2'). $this->Auth->mapActions( array( 'create' => array('ciertaAccion'). Por ejemplo. echo $form->input('username'). En un ejemplo muy simplificado: Vista: echo $form->create('User'.2.En el campo $hashedPasswords['User']['password'] ahora debería ser realizado el 'hash' usando el método password del componente.6 login login($data = null) Si estás haciendo algún tipo de login basada en Ajax. automáticamente realizará el hash al campo contraseña usando esta función. } . if($this->User->save($this->data)) { // enviar el email de registro conteniendo la contraseña al nuevo usuario $this->Auth->login($this->data). Ejemplo: $this->redirect($this->Auth->logout()). 0 en caso de fallo.2.8 password password (string $password) Pásale una cadena de texto. Este método también es útil si deseas proporcionar un enlace 'Cerrar sesión' dentro de una sección para usuarios registrados de tu aplicación. $this->redirect("inicio"). continuar procesando . 5. 5.5.7 logout Provee de una manera rápida de 'deautenticar' a alguien y redirigirlo a donde necesite ir. } } Una cosa a remarcar es que has de redirigir manualmente al usuario tras el login ya que no se invoca loginRedirect().. $this->Auth->login($data) devuelve 1 tras un login exitoso. y obtendrás la contraseña 'hasheada'.. 'users/registrar'). Esta es una funcionalidad esencial si estás creando una pantala de registro de usuario donde los usuarios han de insertar sus contraseñas una segunda vez para confirmarlas.5. if ($this->data['User']['password'] == $this->Auth->password($this->data['User']['password2'])) { // Las contraseñas concuerdan. $this->data['User']['password'] = $contrasena_asignada.$contrasena_asignada = "ConTr4senna".2. } else { $this->flash('Las contraseñas introducidas no concuerdan'. La información es tomada de la sesión.nombre') //devuelve el valor particular de un campo La clave de la sesión puede ser diferente dependiendo de qué modelo se ha configurado para ser utilizado por Auth. } También puede ser usado para obtener todos los datos de sesión del usuario así: $data['User'] = $this->Auth->user().User. . si usas el modelo Cuenta en vez de User. En la vista puedes utilizar el helper Session para obtener la información del usuario actualmente autenticado: $session->read('Auth. La función de hash utilizada depende de la seleccionada por la clase utilidad del núcleo Security (sha1 por defecto). El valor salt es el indicado en la configuración de tu aplicación definido en tu core.2. P.El componente Auth automáticamente aplicará el hash al campo contraseña (password) si también está presente el campo nombre de usuario (username) en los datos recibidos en la petición. Por ejemplo: if ($this->Auth->user('rol') == 'admin') { $this->flash('Tienes acceso de administrador').e. // devuelve el registro completo del usuario $session->read('Auth. entonces la clave de sesión sería Auth.5..9 user user(string $key = null) Este método proporciona información sobre el usuario actualmente identificado.php. Cake añade tu cadena contraseña a un valor salt y después realiza el hash. Puedes utilizar la función Security::setHash para cambiar el método para calcular el hash. 5. Si este método devuelve null es que el usuario no se ha identificado (logged in).Cuenta.User'). ?> . 'password' => 'passwd'). deberías añadirla a beforeFilter() de AppController. Simplemente cámbialo configurando este valor con el nombre del modelo que deseas usar.2. 'action' => 'inicio_sesion'). ?> 5.activo' => true). <?php $this->Auth->userScope = array('User. Normalmente añades esta configuración en el método beforeFilter() de tu controlador. <?php $this->Auth->fields = array('username' => 'email'.2 fields Sobreescribe los campos de usuario y contraseña por defecto usados para la autenticación.6. <?php $this->Auth->loginAction = array('admin' => false. 5. <?php $this->Auth->userModel = 'Miembro'.6.6.1 userModel ¿No deseas utilizar un modelo User contra el que autenticar? No hay problema.4 loginAction Puedes cambiar el login por defecto de /users/login para que sea cualquier acción a tu elección. ?> 5. Si necesitas aplicar dicha configuración a todo el sitio.6. 'controller' => 'miembros'.2.3 userScope Utiliza esto para añadir requisitos adicionales para que la autenticación sea exitosa. ?> 5.2.5.6 Atributos de AuthComponent Ahora hay varias variables relacionadas con Auth que también puedes utilizar.2.2. 'action' => 'logout').5.8 authError Cambia el mensaje de error por defecto que será mostrado.5 loginRedirect El componente AuthComponent recuerda qué par controlador/acción estabas tratando de ejecutar antes de que pedirte que te autenticaras. you fool! ?> That's not the right password!". almacenando el valor en Session bajo la clave Auth.6 logoutRedirect You can also specify where you want the user to go after they are logged out. 5. 'action' => 'inicio'). 'controller' => 'members'. si este valor de la sesión no está definido (si vienes de la página de login de un enlace externo.2. you are lacking access.2.admin') => false. cuando el login no sea exitoso. por ejemplo).6. ?> 5. with the default being the login action. Sin embargo. ?> . <?php $this->Auth->logoutRedirect = array(Configure::read('Routing. cuando intenten acceder a un objeto o a una acción a la que no autorizada.6.6. ?> 5. Ejemplo: <?php $this->Auth->loginRedirect = array('controller' => 'miembros'. <?php $this->Auth->loginError = "No. entonces el usuario será redirigido a la URL indicada en loginRedirect.2.7 loginError Cambia el mensaje de error por defecto que se mostrará. <?php $this->Auth->authError = "Sorry.2.redirect.6.". just in case we use it. The code present in the login function will only execute after authentication was attempted.code inside this function will execute only when autoRedirect was set to false (i. in a beforeFilter).9 autoRedirect Normally. } . $this->Auth->autoRedirect = false. $cookie['password'] = $this->data['User'] ['password']. true. $cookie['username'] = $this->data['User'] ['username']. $this->Cookie->write('Auth.6. This is the best place to determine whether or not a successful login occurred by the AuthComponent (should you desire to log the last successful login timestamp. } $this->redirect($this->Auth->redirect()). } } } } ?> The code in the login function will not execute unless you set $autoRedirect to false in a beforeFilter.. Sometimes you want to do some more checking before you redirect users: <?php function beforeFilter() { . you can also inject additional code such as keeping track of the last successful login timestamp . if (!is_null($cookie)) { if ($this->Auth->login($cookie)) { // Clear auth message. $cookie.auth'). unset($this->data['User']['remember_me']).. if ($this->Auth->user()) { if (!empty($this->data['User']['remember_me'])) { $cookie = array(). function login() { //-. } if (empty($this->data)) { $cookie = $this->Cookie->read('Auth. '+2 weeks'). $this->Session->delete('Message.. $this->redirect($this->Auth->redirect()). With autoRedirect set to false. etc..User'.2.).e. the AuthComponent will automatically redirect you as soon as it authenticates.User').5. 2. <?php function isAuthorized() { if ($this->action == 'delete') { if ($this->Auth->user('role') == 'admin') { return true.6. date('Y-m-d H:i:s') ). } } return true. you'll need to add a method called isAuthorized() to your controller. } else { return false. This method allows you to do some more authentication checks and then return either true or false.<?php function login() { if( !(empty($this->data)) && $this->Auth->user() ){ $this->User->id = $this->Auth->user('id'). By setting this variable to one of several different values. Here are some of the more common ones you might want to use.10 authorize Normally. $this->redirect($this->Auth->redirect()). } ?> . you can do different things. } } ?> 5. However. ?> When authorize is set to 'controller'. $this->User->saveField('last_login'. the AuthComponent will attempt to verify that the login credentials you've entered are accurate by comparing them to what's been stored in your user model. there are times where you might want to do some additional work in determining proper credentials. <?php $this->Auth->authorize = 'controller'. $action) { switch ($action) { case 'default': return false.. ?> By using crud.. . Auth will make use of ACL and check with AclComponent::check(). $controller. } break. you can use authorize with actions such as below <?php $this->Auth->authorize = 'actions'. Auth will make use of ACL and check with AclComponent::check(). ?> By using actions. ?> Don't want to add anything to your controller and might be using ACO's? You can get the AuthComponent to call a method in your user model called isAuthorized() to do the same sort of thing: <?php class User extends AppModel { . function isAuthorized($user. <?php $this->Auth->authorize = array('model'=>'User'). } } ?> } Lastly. <?php $this->Auth->authorize = 'crud'. break.Remember that this method will be checked after you have already passed the basic authentication check against the user model. case 'delete': if ($user['User']['role'] == 'admin') { return true. Actions should be mapped to CRUD (see mapActions). An isAuthorized function is not needed. 2. Como con cualquier parte de CakePHP.6.2. ?> 5. 5. 5.14 actionPath If using action-based access control. $actionPath should be set to 'Controllers/'.2.{$userModel name}". for example. all controller nodes are nested under an ACO node named 'Controllers'.12 ajaxLogin Si estás haciendo solicitudes basadas en Ajax o Javascript que requieren sesiones autenticadas.11 sessionKey Name of the session array key where the record of the current authed user is stored. asegúrate de revisar la clase AuthComponent para mayores detalles. See Changing the Encryption Type for more info. this defines how the paths to action ACO nodes is computed. .5.6. <?php $this->Auth->sessionKey = 'Authorized'.2.6.2.15 flashElement In case that you want to have another layout for your Authentication error message you can define with the flashElement variable that another element will be used for display.6. establece en esta variable el nombre del elemento vista que deseas mostrar y retornar cuando la sesión es inválida o ha expirado. If. 5. Defaults to "Auth". so if unspecified. the record is stored in "Auth. <?php $this->Auth->flashElement ?> = "message_error".13 authenticate This variable holds a reference to the object responsible for hashing passwords if it is necessary to change/override the default password hashing mechanism.6. Indica que la cookie deberá ser transmitida únicamente por una boolean $secure false conexión segura HTTPS. Si $cookiePath está seteada a '/foo/'. Si el valor es una cadena. Antes de tratar de utilizar el componente Cookie. Puedes configurar esto directamente dentro del método de escritura write(). la cookie estará disponible sólo dentro del directorio string $path '/' /foo/ y todos los subdirectorios (como por ejemplo /foo/bar/) de tu dominio.tudominio. El nombre de dominio habilitado para acceder a la cookie.com' para permitir acceso desde todos los subdominios.5. También incluye una serie de funcionalidades muy útiles para agilizar la escritura de cookies. Los enteros son interpretados int $time o string como segundos. Estas variables especiales generalmente se setean en el método beforeFilter() de tu controlador. El tiempo en que expirará la cookie. ej. Cuando este valor sea true. Esta cadena es utilizada para encriptar el valor escrito en la cookie. y te permiten modificar la forma en que las cookies son creadas y gestionadas. la cookie expira cuando se cierra el navegador. debe asegurarse que se encuentra habilitado en el arreglo $components: si está habilitado uno de los elementos del arreglo debe ser 'Cookie' 5. Use string $domain '' '. es '5 Days' decir. La ruta del servidor donde la cookie será aplicada.3 Cookies El componente Cookie es una abstracción para acceder al método nativo de PHP setcookie(). será interpretada con la función de PHP strtotime(). La string $key null cadena debería ser aleatoria y difícil de adivinar. Puedes configurar esto directamente dentro del método de escritura write() El siguiente recorte del código de un controlador muestra cómo incluir el componente Cookie y cómo configurar las variables necesarias para enviar una cookie llamada 'baker_id' para el dominio . y un valor 0 es equivalente a 'cookie de sesión'. Puedes configurar esto directamente dentro del método de escritura write(). la cookie será creada sólo si existe una conexión segura.1 Configuración del Controlador Hay una serie de variables que se configuran en el controlador. La opción por defecto es en todo el dominio.3. Variable Cookie string $name por defecto descripción 'CakeCookie' El nombre de la cookie. $key es la variable que se quiere guardar en la cookie y $value es el dato para almacenar.3. $this->Cookie->secure = true. Todos los valores de las cookis son encriptados por defecto. $this->Cookie->write('nombre'. $this->Cookie->domain = 'example.'Pepito'. write(mixed $key.2 Utilizando el Componente Esta sección resume los métodos del componente Cookie. y expira en una hora. $this->Cookie->time = 3600. } //enviar sólo por una conexión segura HTTPS $this->Cookie->key = 'qSI232qs*&sXOw!'. cambie el tecer parámetro del método write() a false. A continuación. mixed $value. mixed $expires) El método write() es el corazón del componente.'example. Si deseas escribir más de un valor a la vez en la cookie. Si desea almacenar valores en texto puro.'Pepito').com'.rol'. También puedes agrupar variables utilizando la notación de 'punto' en el parámetro $key. debe estar disponible para la ruta ‘/bakers/preferencias/’. puedes pasar un arreglo: $this->Cookie->write( array('nombre'=>'Pepito'.com' que a su vez necesita una conexión segura. $this->Cookie->write('Usuario. // o '1 hour' $this->Cookie->path = '/bakers/preferencias/'. 'Pepito'). 5. var $components = array('Cookie').'Lider'). . veremos cómo utilizar los diferentes métodos del componente Cookie. $this->Cookie->write('nombre'.nombre'. function beforeFilter() { $this->Cookie->name = 'baker_id'. boolean $encrypt. $this->Cookie->write('Usuario.'rol'=>'Lider') ).false). //esto devuelve un arreglo similar a array('nombre' => 'Pepito'. La variable a leer debe ser especificada en el parámetro $key. //También se puede utilizar la notación de 'punto' para leer echo $this->Cookie->read('Usuario.false.nombre'). read(mixed $key) Este método es utilizado para leer el valor de una variable almaenada en una cookie. //Borrar una variable $this->Cookie->del('bar') //Borrar la variable bar. este parámetro puede ser pasado como una cadena que entienda la función strtotime() de PHP: //Ambas cookies expiran en una hora. '1 hour'). $this->Cookie->write('Apellido'. . 3600). $this->Cookie->write('nombre'. También funciona con la notación de 'punto'. 'rol'=>'Lider') del(mixed $key) Borra el contenido de la variable $key almacenada en una cookie.false.El último parámetro del método es $expires: el número de segundos antes de que la cookie expire. Por convenienia.'Pepito'.bar') destroy() Destruye la cookie actual. //Para obtener las variables que has agrupado utilizando //la notación de 'punto' como un arreglo debes usar $this->Cookie->read('Usuario'). //Muestra “Pepito” echo $this->Cookie->read('name').'Gonzales'. pero no todas las contenidas en foo $this->Cookie->del('foo. html(código HTML) o both(ambos). sendAs attachments Arreglo de archivos a enviar (rutas absolutas y relativas) Como enviar el mensaje (mail. 5. (integer) Como quieres mandar el mensaje: text(texto). También soporta archivos adjuntados y inclusión/filtrado simple de encabezados. (port(puerto).4. etc. username(nombre de usuario). o ambos. . html.ctp. password(contraseña)) Hay algunas opciones más para configurar. timeout(tiempo de espera). archivos .1 Atributos y Variables de la clase Estos son los valores que puedes configurar antes de hacer la llamada EmailComponent::send() to cc bcc replyTo from subject template dirección a la que se dirige el mensaje (string) arreglo de direcciones a enviar copias del mensaje (CC) arreglo de direcciones a enviar las copias ocultas del mensaje (CCO) dirección de respuesta(string) dirección remitente (string) asunto del mensaje (string) Elemento email a usar para el mensaje(ubicado app/views/elements/email/html/ y en en layout app/views/elements/email/text/) Layout usado por el mail (ubicado en app/views/layouts/email/html/ y en app/views/layouts/email/text/) lineLength Longitud (en caracteres) en la que corta las líneas largas. formateados como texto. para mayor información consulta la documentación de CakePHP.4 Email El componente Email es una manera simple de agregarle a tu aplicación CakePHP la funcionalidad de envío de mails. Puede enviar mails por medio de las funciones propias de PHP. Por defecto es 70. Hay un montón de cosas que no hace por tí. smtpOptions host(servidor). usando los mismos conceptos de layouts. vistas. pero te pondrá en movimiento. smtp [requerirá el campo smtpOptions explicado delivery abajo] y debug) Arreglo asociativo de opciones para el envío por SMTP. vía servidor SMTP o en modo DEBUG en el que escribe el mensaje en un mensaje flash de sesión.5. 2 Envío de un mensaje simple Para enviar un mensaje sin usar ningún template.4. In order to view those debugging information you need to create an extra line in your view or layout file (e. underneath your normal flash message in /layouts/default.com>'. Tal como lo hayas hecho para tu layout default para las vistas en un navegador. $this->Email->to = 'Alguien más <alguien.g. Por ejemplo: $this->Email->from = 'Alguien <
[email protected]>'.4.1 Envío múltiple de emails en bucle Si lo que quieres es enviar varios emails usando un bucle. En la carpeta app/views/layouts/ precisas tener (como mínimo) esta estructura email/ html/ text/ default. sólo pasa el cuerpo del mensaje como una cadena (string) o un arreglo de líneas al método send().1 Configurando el Layout Para usar tanto texto como html en el email necesitarás crear los archivos de layout para ellos.2.ctp): <?php echo $this->Session->flash().4.ctp default.1. $this->Email->subject = 'Prueba'.5.2 Debugging Emails If you do not want to actually send an email and instead want to test out the functionality.1. ?> 5.4. 5. precisas establecer los layouts default para tus emails.ctp . $this->Email->send('Hola cuerpo de mensaje!!!'). Necesitarás resetearlo antes de setear nuevamente las propiedades del email. ?> <?php echo $this->Session->flash('email'). deberás resetear los campos de mails usando el método reset() del componente Email. $this->Email->reset() 5. you can use the following delivery option: $this->Email->delivery = 'debug'.mas@ejemplo. Algunos ejemplos simples a continuación.<br />  . En cada una de estas carpetas debes crear templates para poder utilizar con el contenido que le envíes a la vista ya sea usando $this->set() o usando el parámetro $contents del método send(). ' ' .Estos son los archivos que conservan los valores por defecto para los templates de layout para tus mensajes. ?> </body> </html> 5.ctp <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4. ' ' . $User['last'] ?>.Gracias por su interés.ctp <?php echo $content_for_layout.ctp text Estimado <?php echo $User['first']. $User['last'] ?>. Un simple ejemplo de contenido: email/text/default.0 Transitional//EN"> <html> <body> <?php echo $content_for_layout. ?> email/html/default. .2 Configurar un elemento Email para el cuerpo del mensaje En el directorio app/views/elements/email/ debes configurar carpetas para text(mails modo texto) y html(mails modo HTML) a menos que quieras usar sólo uno de ellos. html <p>Estimado <?php echo $User['first'].2.4. Gracias por su interés.</p> . usando el template simple_message. . $this->Email->to = $User['User']['email']. 'text' or 'both' (ambos) . // NOTAR QUE NO HAY '. $this->Email->subject = 'Bienvenido a nuestra cosa genial'. podrías llamarlo desde otro método de esta forma: Plain Text View $this->_sendNewUserMail( $this->User->id ). $this->Email->bcc = array('
[email protected]'.2.5. En nuestro controlador (usemos el controlador User en este ejemplo): <?php function _sendNewUserMail($id) { $User = $this->User->read(null. // queremos enviar un lindo email //Variables de la vista $this->set('User'. } ?> Has enviado un mensaje.$id).com>'.com'). $this->Email->replyTo = '
[email protected]. . $this->Email->template = 'simple_message'. ?> En este ejemplo configuraremos un método privado para manejar el envío de mensajes de email a un usuario identificado por un $id.ctp' //Enviar como 'html'. $this->Email->from = 'Cool Web App <app@ejemplo. $User).(por defecto es 'text') $this->Email->sendAs = 'both'.3 Controlador En tu controlador necesitas agregar el componente a tu array $components o agregar un array $components a tu controlador de la forma: <?php var $components = array('Email'). //NO PASAMOS ARGUMENTOS A SEND() $this->Email->send(). /* Configurar método de entrega */ $this->Email->delivery = 'smtp'. 'username'=>'tu_nombre_usuario_smtp'.doc' => TMP .smtp'. $this->Email->smtpError). 'host' => 'tu. También puedes obtener los errores SMTP generados durante la sesión leyendo la propiedad smtpError del componente. /* No le pases ningún argumento a send() */ $this->Email->send().doc will be attached with the same filename.5.4 Attachments Here's how you can send file attachments along with your message.doc will be be used for attaching instead of its actual filename some-temp-name 5. . los pasos a seguir son similares a los del mensaje básico. The first file foo. 'foo.doc'. 'bar.4.3 Enviar un mail por SMTP Para enviar un mail usando servidor SMTP.servidor. Si tu servidor SMTP requiere autenticación.4. 'some-temp-name' ). Configurar el método de entrega (delivery) a smtp y asignar las opciones a las propiedades del objeto de Email smtpOptions. /* Chequeo de errores SMTP. 'password'=>'tu_contraseña_smtp'). 'timeout'=>'30'.2. /* Opciones SMTP*/ $this->Email->smtpOptions = array( 'port'=>'25'. asegúrate de especificar los parámetros de nombre de usuario y contraseña en smtpOptions como se ve en el ejemplo. */ $this->set('smtp-errors'. For the second file we specify an alias bar. $this->Email->attachments = array( TMP . You set an array containing the paths to the files to attach to the attachments property of the component. RequestHandler detectará automáticamente las peticiones Ajax basadas en la cabecera HTTP-X-Requested-With que utilizan muchas librerías javascript. se parsearán en un objecto XML que se asigna a Controller::data. accepts will return true if the client accepts the content type. RequestHandler cambiará automáticamente los ficheros de layout y vista a aquellos que coincidan con el tipo pedido. If an array is specified. debe ser incluído en tu array $components. 'rss'. or null. y que puede entonces ser salvado como datos de modelo. accepts ( $type = null) $type can be a string. Cuando se utiliza junto con Router::parseExtensions(). como también para obtener información adicional sobre tipos de contenido que el cliente acepta y cambiar automáticamente al layout apropiado cuando estén activadas las extensiones de ficheros. <?php class WidgetController extends AppController { var $components = array('RequestHandler'). If null returns an array of the content-types that the client accepts. Puedes usarlo para informar a tus controladores sobre Ajax.5. function beforeFilter () { if ($this->RequestHandler->accepts('html')) { // Execute code only if client accepts an HTML (text/html) response } elseif ($this->RequestHandler->accepts('xml')) { // Execute XML-only code } if ($this->RequestHandler->accepts(array('xml'. si existe un helper con el mismo nombre que la extensión pedida. Por defecto. If a string. or an array.1 Obtaining Request Information Request Handler has several methods that provide information about the client and its request. accepts return true if any one of the content types is accepted by the client. RSS or Atom } }} . si son "POSTeados" datos XML a tus controladores. Para poder hacer uso de Request handler.5. For example: class PostsController extends AppController { var $components = array('RequestHandler'). Finalmente. 'atom'))) { // Executes if the client accepts any of the above: XML. //resto del controlador } ?> 5. Además.5 Request Handling El componente Request Handler (manejador de la petición) es usado en CakePHP para obtener información adicional sobre las peticiones HTTP que son realizadas a tus aplicaciones. se añadirá al array de helpers del controlador. The supported Mobile User Agent strings are: • iPhone • MIDP • AvantGo • BlackBerry • J2ME • Opera Mini • DoCoMo • NetFront • Nokia • PalmOS • PalmSource • portalmmm • Plucker • ReqwirelessWeb . isSSL() Returns true if the current request was made over an SSL connection. or if the client accepts WAP content.Other request 'type' detection methods include: isAjax() Returns true if the request contains the X-Requested-Header equal to XMLHttpRequest. isXml() Returns true if the current request accepts XML as a response. false otherwise. isMobile() Returns true if user agent string matches a mobile web browser. isRss() Returns true if the current request accepts RSS as a response. isAtom() Returns true if the current call accepts an Atom response. $this->header('Cache-control: no-cache'). . For example when responding to Ajax requests. However. The following would accomplish that: if ($this->RequestHandler->isAjax()) { Configure::write('debug'.• SonyEricsson • Symbian • UP. } //Continue Controller action You could also disable caching with the functionally analogous Controller::disableCache if ($this->RequestHandler->isAjax()) { $this->disableCache(). 26 Jul 1997 05:00:00 GMT"). and change the debug level. you often will want to disable browser caching. $this->header("Expires: Mon.Browser • Windows CE • Xiino isWap() Returns true if the client accepts WAP content. All of the above request detection methods can be used in a similar fashion to filter functionality intended for specific content types.5. } //Continue Controller action 5. 0). $this->header('Pragma: no-cache').2 Request Type Detection RequestHandler also provides information about what type of HTTP request has been made and allowing you to respond to each Request Type. you want to allow caching for non-ajax requests. isPost() Returns true if the request is a POST request. 3 Obtaining Additional Client Information getClientIP() Get the remote client IP address getReferer() Returns the domain name from which the request originated getAjaxVersion() Gets Prototype version if call is Ajax. these content types are used by prefers() and accepts(). If you are using Router::parseExtension. css.5. This allows RequestHandler to automatically respond to requests of each type in its startup method. setContent adds/sets the Content-types for the given name. $type = null) • $name string . The Prototype library sets a special "Prototype version" HTTP header. 5. isGet() Returns true if the request is a GET request. setContent is best used in the beforeFilter() of your controllers. Furthermore. html. json. 5.isPut() Returns true if the request is a PUT request.5. as this will best leverage the .The name or file extension of the Content-type ie.The mime-type(s) that the Content-type maps to. you should use the file extension as the name of the Content-type. otherwise empty string. setContent($name. • $type mixed . Allows content-types to be mapped to friendly aliases and or extensions. xml. isDelete() Returns true if the request is a DELETE request.4 Responding To Requests In addition to request detection RequestHandler also provides easy access to altering the output and content type mappings for your application. wml • wmlscript text/vnd.wap. image/vnd. text/plain • form application/x-www-form-urlencoded • file multipart/form-data • xhtml application/xhtml+xml.wbmp • pdf application/pdf • zip application/x-zip • tar application/x-tar prefers($type = null) Determines which content-types the client prefers. application/xhtml.wap.ms-excel.wap. The default mappings are: • javascript text/javascript • js text/javascript • json application/json • css text/css • html text/html.wap.wmlscript • wbmp image/vnd. .wap.wap.xhtml+xml • xml application/xml. If $type is an array the first type the client accepts will be returned.wap.wmlscript. and secondly by the list of content-types in HTTP_ACCEPT.wml. Preference os determined primarily by the file extension parsed by Router if one has been provided.wbmp • wml text/vnd. text/xml • rss application/rss+xml • atom application/atom+xml • amf application/x-amf • wap text/vnd. text/xhtml • xhtml-mobile application/vnd. If no parameter is given the most likely content type is returned.automagicness of content-type aliases. text/vnd. */* • text text/plain • txt text/plain • csv application/vnd. y tiene distintos parámetros para ajustar. mapType($ctype) Maps a content-type back to an alias 5.1 Configuración $blackHoleCallback Un callback del controlador que se encargará de manejar las peticiones que hayan sido enviadas al "agujero negro". y que se llaman de la misma manera. Todas esas propiedades pueden configurarse directamente con los métodos creados para tal fin.Friendly content type name ex.renderAs($controller. Sets the response header based on content-type map names. the header is not set. Will also append the appropriate helper to the controller's helper array if available and not already in the array. xml. responseType() Returns the current response type Content-type header or null if one has yet to be set. respondAs($type. $type) • $controller .6. rss. xml. Con el componente Security se puede crear una intefaz para manejar autenticaciones HTTP.If $type is a friendly type name that has more than one content association. rss or a full content type like application/xshockwave • $options . If DEBUG is greater than 1. $options) • $type . . Change the render mode of a controller to the specified type. Se configura en el método beforeFilter() de tus controladores. $index is used to select the content type.Controller Reference • $type . 5.friendly content type name to render content for ex.6 El Componente Security El componente Security provee una forma fácil de aumentar la seguridad en tu aplicación. También acepta '*' indicando que todas las acciones del controlador requieren autenticación HTTP. $loginUsers Un arreglo asociativo de usuarios => passords que sonusados para autenticaciones HTTP. $requireLogin Una lista de acciones que requieren autenticación HTTP (basic o diges). $loginOptions Opciones para la autenticación HTTP. $requireAuth Una lista de acciones que requieren una clave de autenticación válida. También puede utilizarse para peticiones entre controladores. Un arreglo de acciones del controlador o '*' para forzar que todas las acciones requieran POST. Un arreglo de acciones del controlador o '*' para forzar que todas las acciones requieran una conexión SSL. . Esta clave es configurada por el componente Security. Si estás utilizando autenticacion digest.$requirePost Una lista de acciones del controlador que requieren una petición POST para ejecutarse. y los callbacks del controlador para el proceso de autenticación. $disabledFields Campos del formulario que se necesitan deshabilitar para una petición dada. los passwords deben ser encryptados con el algoritmo MD5. $allowedActions Una lista de acciones desde las cuales las acciones del controlador actual pueden para recibir peticiones. $allowedControllers Una lista de controladores desde los cuales la acción del controlador actual puede recibir peticiones. También puede utilizarse para peticiones entre controladores. Permite definir que tipo de autenticación utilizar. $requireSecure Lista de acciones que requieren una conexión SSL para ejecutarse. 5. 5. Acepta cualquier número de argumentos. Acepta cualquier cantidad de argumentos. Puede ser llamado sin argumentos para forzar a que todas las acciones requieran una petición POST. $options generally contains a 'type'.2 requireSecure() Define las acciones que requieren una conexión SSL presente. o 'digest'. Realm defaults to the current HTTP server environment.6. 5.6.6 loginRequest(array $options) Generates the text for an HTTP-Authenticate request header from an array of $options.6.6. . Puede ser llamado sin argumentos para forzar que todas las acciones requieran una conexión SSL.6.2. 5.2 Métodos 5. tratará de verificar las dos. Si se deja como null o vació. Acepta cualquier número de argumentos. Puede ser llamado sin argumentos para forzar que todas las acciones requieran una autenticación válida. 5. Acepta cualquier número de argumentos.2. 'realm' .2. Puede ser 'basic'.2.6. Type indicate which HTTP-Authenticate method to use. 5. Puede ser llamada sin argumentos para forzar que todas las acciones requieran una autenticación HTTP válida. Devuelve un arreglo con el nombre de usuario y el password si tuvo éxtito.3 requireAuth() Define las acciones que requieren un token válido generado por el componente Security. $type es el tipo de autenticación que se desea verificar.5 loginCredentials(string $type) Trata de validar las credenciales para una petición de autenticación HTTP.6.2.1 requirePost() Define las acciones que requieren una petición POST.2.4 requireLogin() Define las acciones que requieren una autenticación HTTP válida. 6. With no callback. If a controller callback is set to SecurityComponent::blackHoleCallback.8 generateDigestResponseHash(array $data) Creates a hash that to be compared with an HTTP digest-authenticated response. string $error) Black-hole an invalid request with a 404 error or a custom callback. <?php class WidgetController extends AppController { var $components = array('Security'). . it will be called and passed any error information.2. and null on failure.3 Uso EL uso del componente security generalmente se hace en el método beforeFilter() del controlador. function beforeFilter() { $this->Security->requirePost('delete').6.2. 5.2. <?php class WidgetController extends AppController { var $components = array('Security').7 parseDigestAuthData(string $digest) Parse an HTTP digest authentication request.6. 5. Usted especifica las restricciones de seguridad que desee y el componente Security las hara cumplir en el arranque. Returns and array of digest data as an associative array if succesful. the request will be exited. $data should be an array created by SecurityComponent::parseDigestAuthData().6. 5.5. } } ?> En este ejemplo la accion delete solo puede ser lanzada satisfactoriamente si se recive una solicitud POST.9 blackHole(object $controller. . function beforeFilter() { $this->Security->loginOptions = array( 'type'=>'basic'. One common usage for HTTP Auth is protecting a REST or SOAP API.function beforeFilter() { if(isset($this->params[Configure::read('Routing.4 Basic HTTP Authentication The SecurityComponent has some very powerful authentication features. } } } ?> Este ejemplo forzaría todas las acciones que tengan enrrutamiento admin a requerir peticiones seguras del SSL.6. Sometimes you may need to protect some functionality inside your application using HTTP Basic Authentication. The code example below includes the SecurityComponent and adds a few lines of code inside the controller's beforeFilter method. var $components = array('Security'). credentials will be transferred in plain text. 'jane'=>'janespassword' ). Using the SecurityComponent for HTTP authentication is easy. 'realm'=>'MyRealm' ). var $uses = array(). This type of authentication is called basic for a reason. 5. Unless you're transferring information over SSL. $this->Security->loginUsers = array( 'john'=>'johnspassword'.admin')])){ $this->Security->requireSecure(). class ApiController extends AppController { var $name = 'Api'. también deberías . Actúa como una abstracción para acceder a las variables $_SESSION. y a su vez. database Guarda la información dentro de la base de datos. Las sesiones pueden persistirse de diferentes maneras. The loginUsers property of the SecurityComponent is an associative array containing users and passwords that should have access to this realm. requireLogin() tells SecurityComponent that this Controller requires login. Guarda la información como se indica en php.ini Para cambiar el método por defecto de gestión de sesiones.7 Sesiones El componente Session de CakePHP provee una forma de persistir datos entre las peticiones de las páginas. 5. } function index() { //protected application logic goes here. Si eliges 'database'.. Finally. Specify the realm if you want display a nice message to anyone trying to login or if you have several authenticated sections (= realms) of your application you want to keep separate. php La opción por defecto. Por defecto se utilizan los métodos provistos por PHP. The examples here use hard-coded user information. debes modificar el parámetro de configuración Session. providing method names will protect those methods while keeping others open. above. } } The loginOptions property of the SecurityComponent is an associative array specifying how logins should be handled. You only need to specify the type as basic to get going. As with requirePost().. agrega distintos métodos útiles relacionados con este arreglo. but you'll probably want to use a model to make your authentication credentials more manageable.$this->Security->requireLogin().save para reflejar la opción deseada. existen otras opciones: cake Guarda los archivos de la sesión en el directorio temporal de tu aplicación tmp/sessions. sin embargo. 7. $key = 'flash') Se utiliza para guardar una variable de sesión que puede ser mostrada en la vista. 'Verde'). 5. dentro de Persona => colorOjos.com') ). Baja y Modificación) como también opciones para comunicarse con los usuarios.7.1.descomentar el parámetro Session. Escribe el valor 'Verde' en la sesión.1 write write($name. 'default'.2 setFlash setFlash($message.1 Métodos El componente Session es usado para interactuar con la información de la sesión. $layout permite controlar qué layout (ubicado en /app/views/layouts) se utilizará para mostrar el mensaje. Los puntos son usados para indicar arreglos anidados. 5. Incluye operaciones básicas de ABM (Alta. $value) Guarda en la sesión una variable llamada $name con el valor $value. Por ejemplo: $this->Session->write('Persona.database y ejecutar el archivo SQL perteneciente a la sesión localizado en app/config En views/elements la sesión puede ser accedida por medio del helper Session 5.colorOjos'.1. De esta forma. el mensaje se mostrará como se ve a continuación: <div id="flashMessage" class="message"> [message] </div> .email equivaldrá a: array('Usuario' => array('email' => 'clarkKent@dailyplanet. Si la variable $layout queda configurada con su valor por defecto. Debe notarse que se pueden crear estructuras de arreglos en las sesiones utilizando la notación de 'punto'. $params = array(). $name puede utilizar la notación de 'punto'.7. Usuario. $layout = 'default'. Devuelve true en caso afirmativo. 5. Nuestra información de sesión ya no tiene el valor 'Verde'. 5. Ej. se devolverá todo el contenido de la sesión.colorOjos'). $key es utilizado para modificar el índice del arreglo $message en el arreglo de mensajes (por defecto es 'flash').1.colorOjos'). por ejemplo pasando un parámetro 'class' en el arreglo $params. $message es el texto que se quiere cargar en la sesión. se modificará el div resultante de utilizar el método $session->flash() en la vista. setFlash('Mensaje de ejemplo'. $this->Session->delete('Persona.5 delete delete($name) Borra los datos de la variable $name dentro de la sesión. $verde = $this->Session->read('Persona.4 check check($name) Se utiliza para verificar si una variable ha sido seteada en la sesión. array('class' => 'clase_ejemplo')) La salida de $session->flash() del ejemplo anterior será: <div id="flashMessage" class="clase_ejemplo">Mensaje de ejemplo</div> 5.7. Ej.1. Recupera el valor 'Verde' de la sesión.7. y false si la variable nunca fue seteada. Si $name es null. Los parámetros puede afectar el div que se muestre.7. ni siquiera la clave 'colorOjos'. 'default'. Sin .1.3 read read($name) Devuelve el contenido de la variable $name dentro de la sesión.$params permite pasar variables adicionales para mostrar en el layout. 1. .6 destroy El método destroy eliminará la cookie de sesión y todos los datos almacenados en los archivos temporales del sistema. Persona todavía existe en la sesión.7.7. 5. $this->Session->destroy() 5.7 error error() Se utiliza para determinar el último error en una sesión.embardo. Para eliminar la variable completa de la sesión se debe usar: $this->Session->delete('Persona').1. Destruirá la sesión de PHP y creará una sesión nueva. Para usar el nuevo comportamiento. Cuando lo agregas al arreglo $actsAs. El método parentNode() del modelo debe retornar null.1. de la siguiente forma: $this->Post->Behaviors->attach('Acl'. se pueden elegir entre hacer la entrada actual como un ARO o un ACO. } Se puede agregar el comportamiento ACL al vuelo. se debe usar: class Post extends AppModel { var $actsAs = array('Acl' => array('type' => 'controlled')). . usarlo requiere que tu Modelo tenga el método parentNode() definido. CakePHP viene con un numero de comportamientos incorporados tales como el Arbol (Tree) y Contenible (Containable). retornar una referencia al modelo padre. Esto es usado por el comportamiento ACL para determinar las relaciones padre-hijo.1 ACL El comportamiento Acl provee una forma de integrar un modelo con tu sistema ACL. array('type' => 'controlled')). Sin embargo. o bien. puedes añadirlo a la propiedad $actsAs de tu modelo. 6. 6. Para que el comportamiento ACL sea ACO.6 Core Behaviors (Comportamientos Basicos) Los comportamientos (Behaviors) agregan funcionalidad extra a tus modelos.1 Using the AclBehavior Muchos de los comportamientos ACL funcionan transparentemente en el método afterSave() del Modelo. } Esto incluye el comportamiento de Acl en el modo ARO. Puede crear tanto los AROs o los ACOs transparentemente. El valor por defecto es para crear AROs. class User extends AppModel { var $actsAs = array('Acl' => array('type' => 'requester')). } if (!$data['User']['group_id']) { return null. } else { $this->Group->id = $data['User']['group_id']. if (empty($this->data)) { $data = $this->read(). $groupNode = $this->Group->node(). } Si se quiere setear un nodo ACO o ARO como padre del modelo. return array('Group' => array('id' => $groupNode[0]['Aro'] ['foreign_key'])). function parentNode() { if (!$this->id && empty($this->data)) { return null.function parentNode() { return null. function parentNode() { return 'root_node'. donde User tiene la relación belongsTo con el modelo Group. Usando un modelo User. El comportamiento Acl usa esta data para construir la estructura del árbol. } } En el ejemplo de arriba el valor de retorno es un arreglo que tiene la misma estructura que el resultado de la operación find() del modelo. . } $data = $this->data. parentNode() debe retornar el alias del nodo ACO o ARO. Es importante setear el valor del id o la relacion parentNode fallará. } Un ejemplo más completo. $this->User->id = 1.2 core is the ContainableBehavior. you can add it to the $actsAs property of your model: class Post extends AppModel { var $actsAs = array('Containable'). It does this by using supplied the containments to generate a series of bindModel and unbindModel calls. Ambos retornaran la misma información del nodo ACL. Using Containable will help you cut down on needless wear and tear on your database. It works by temporarily or permanently altering the associations of your models. pasandolo dentro de un arreglo.6. Containable allows you to streamline and simplify operations on your model bindings. To use the new behavior.2 Containable A new addition to the CakePHP 1.2 node() El comportamiento ACL también permite rescatar el nodo ACL asociado al registro del modelo.1. increasing the speed and overall performance of your application. The class will also help you search and filter your data for your users in a clean and consistent way. } You can also attach the behavior on the fly: $this->Post->Behaviors->attach('Containable'). This model behavior allows you to filter and limit model find operations. $node = $this->User->node($user). 6. $node = $this->User->node(). Despues de setear $model->id se puede utilizar $model->node() para rescatar el nodo ACL asociado. Tambien se puede rescatar el nodo ACL de cualquier fila. $user = array('User' => array( 'id' => 1 )). . . we'll start off with a find() call on a model named Post.net [website] => http://example. . [0] => Array ( ) [1] => Array ( [Post] => Array( [id] => 1 [title] => First article [content] => aaa [created] => 2008-05-18 00:00:00 ) [Comment] => Array( [0] => Array ( [id] => 1 [post_id] => 1 [author] => Daniel [email] => dan@example. let's look at a few examples.net [comment] => Second comment [created] => 2008-05-18 00:00:00 ) ) [Tag] => Array( [0] => Array ( [id] => 1 [name] => Awesome ) [1] => Array ( [id] => 2 [name] => Baking ) ) [Post] => Array (. and Post hasAndBelongsToMany Tag.com [website] => http://example.com [comment] => First comment [created] => 2008-05-18 00:00:00 ) [1] => Array ( [id] => 2 [post_id] => 1 [author] => Sam [email] => sam@example.. Let's say that Post hasMany Comment.# Using Containable To see how Containable works. The amount of data fetched in a normal find() call is rather extensive: debug($this->Post->find('all')). First. $this->Post->find('all'). you can do that without the ContainableBehavior doing something like this: $this->Post->recursive = -1. $this->Post->find('all'). Containable really shines when you have complex associations. you end up with something a lot more concise: [0] => Array ( ) [1] => Array ( [Post] => Array( [id] => 1 [title] => First article [content] => aaa [created] => 2008-05-18 00:00:00 ) [Post] => Array( [id] => 2 [title] => Second article [content] => bbb [created] => 2008-05-19 00:00:00 ) ) This sort of help isn't new: in fact. You can also invoke Containable's magic from inside the find() call: $this->Post->find('all'. or an array of names. If we wanted to fetch all posts and their related tags (without any comment information). Having done that. of the models to keep in the find operation. but not when you want to pick and choose what to keep at each level. and you want to pare down things that sit at the same level.For some interfaces in your application. we'd try something like this: . Let's see how it works by using the contain() method. For example. array('contain' => false)). you can do the following: $this->Post->contain(). The model's $recursive property is helpful if you want to hack off an entire level of recursion. The contain method's first argument accepts the name. you may not need that much information from the Post model. to get only the post-related information. One thing the ContainableBehavior does is help you cut down on what find() returns. The output of the find call might look something like this: [0] => Array( [Post] => Array( [id] => 1 [title] => First article [content] => aaa [created] => 2008-05-18 00:00:00 ) [Comment] => Array( [0] => Array( [author] => Daniel [post_id] => 1 ) [1] => Array( [author] => Sam [post_id] => 1 ) ) ) [1] => Array (.$this->Post->contain('Tag').. we've told Containable to give us our post information. you'd end up needing to use the unbindModel() method of the model. If you are interested in the posts and the names of the comment authors — and nothing else — you could do something like the following: $this->Post->contain('Comment. $this->Post->find('all'.author')). array('contain' => 'Tag')). Without Containable. If you look at the results of the original find() call.author'). # Containing deeper associations Containable also goes a step deeper: you can filter the data of the associated models. array('contain' => 'Comment. Containable creates a cleaner way to accomplish this same task. $this->Post->find('all'). Again. and just the author field of the associated Comment model.. $this->Post->find('all'). we can use the contain key inside a find() call: $this->Post->find('all'. . Here. notice the author field in the Comment model.. multiple times if you're paring off multiple models. //or. com [website] => http://example.As you can see. //or. This gives us a result that gives us posts with comments authored by Daniel: [0] => Array ( [Post] => Array ( [id] => 1 [title] => First article [content] => aaa [created] => 2008-05-18 00:00:00 ) [Comment] => Array ( [0] => Array ( [id] => 1 [post_id] => 1 [author] => Daniel [email] => dan@example. . $this->Post->find('all'.author = "Daniel"')...author = "Daniel"')).author =' => "Daniel"). array('contain' => array( 'Comment' => array( 'conditions' => array('Comment. the Comment arrays only contain the author field (plus the post_id which is needed by CakePHP to map the results). Here's an example of using the ContainableBehavior when you've got deep and complex model relationships. 'order' => 'Comment. You can also filter the associated Comment data by specifying a condition: $this->Post->contain('Comment.com [comment] => First comment [created] => 2008-05-18 00:00:00 ) ) ) Additional filtering can be performed by supplying the standard Model->find() options: $this->Post->find('all'.created DESC' ) ))). array('contain' => 'Comment. $this->Post->find('all'). .name LIKE' => '%happy %') ) ) ) )). array( 'contain'=>array( 'Profile'. you don't to use 'contain' again for related models When using 'fields' and 'contain' options .be careful to include all foreign keys that your query directly or indirectly requires. 'Account' => array( 'AccountSummary' ). you may consider attaching it to your AppModel. 6. Keep in mind that contain key is only used once in the main model.Let's consider the following model associations: User->Profile User->Account->AccountSummary User->Post->PostAttachment->PostAttachmentHistory->HistoryNotes User->Post->Tag This is how we retrieve the above associations with Containable: $this->User->find('all'. 'PostAttachmentHistory' => array( 'HistoryNotes' => array( 'fields' => array('id'. 'Post' => array( 'PostAttachment' => array( 'fields' => array('id'.2. 'Tag' => array( 'conditions' => array('Tag. 'name'). 'note') ) ) ). Please also note that because Containable must to be attached to all models used in containment.1 Using Containable with pagination By including the 'contain' parameter in the $paginate property it will apply to both the find('count') and the find('all') done on the model See the section Using Containable for further details. # ContainableBehavior options The ContainableBehavior has a number of options that can be set when the Behavior is attached to a model. $users = $this->paginate('User'). • recursive (boolean. The default value is true. $this->paginate['User'] = array( 'contain' => array('Profile'. $this->Post->Behaviors->attach('Containable'. optional) set to true to allow containable to automatically determine the recursiveness level needed to fetch specified models. optional) issues E_NOTICES for bindings referenced in a containable call that are not valid. try disabling the autoFields setting. . setting it to false disables this feature. The default value is true. The settings allow you to fine tune the behavior of Containable and work with other behaviors more easily. The default value is true.Here's an example of how to contain associations when paginating. 'Account'). • autoFields: (boolean. You can change ContainableBehavior settings at run time by reattaching the behavior as seen in Using behaviors ContainableBehavior can sometimes cause issues with other behaviors or queries that use aggregate functions and/or GROUP BY statements. and set the model recursiveness to this level. 'order' => 'User. If you get invalid SQL errors due to mixing of aggregate and non-aggregate fields. • notices (boolean.username' ). optional) auto-add needed fields to fetch requested bindings. array('autoFields' => false)). 1 Inicializando las tablas de la Base de datos i18n Puede utilizar la consola de CakePHP o puede crearlo manualmente. . Si lo haces por consola. pues es necesario configurar algunas opciones antes de comenzar a funcionar./cake i18n Seleccione [I] y se ejecuta el script e inicializa la Base de datos i18n.3. En esta sección. 6.2 Adjuntando el Comportamiento de Traducción a tus Modelos Se debe incorporar al modelo haciendo uso de la propiedad $actsAs como en el siguiente ejemplo.6. 6. si usted esta seguro de que no existe una tabla i18n. puedes estar seguro que tiene el correcto diseño. <?php class Post extends AppModel { var $name = 'Post'.3 Translate TranslateBehavior es bastante fácil de configurar y trabaja fuera de la caja con muy poca configuración. Se recomienda utilizar la consola para esto. por que podrían pasar que hallan cambios de diseño en las futuras versiones de CakePHP. y responda con Sí para crear la tabla de nuevo. Se deben definir qué campos de el modelo actual serán rastreados en la tabla de traducciones creada en el paso anterior. Se le preguntará si desea eliminar cualquiera que exista y si desea crearla. .Responde un Sí. } ?> Esto no hará nada aún. var $actsAs = array( 'Translate' ). usted aprenderá cómo añadir y configurar el comportamiento (behavior) para usarlo en cualquier modelo.3. var $actsAs = array( 'Translate' => array( 'nombre' ) ).3.4 Conclusiones Desde ahora en cada actualización/creación de un registro hará que TranslateBehavior copie el valor "nombre" en la tabla de traducciones (por defecto: i18n) de acuerdo a la localización actual.3. Una localización corresponde al identificador de una lengua La localización actual es el valor actual de Configure::read('Config. 'campoDos'.language'). 'campoN' ) ). . } ?> 6.3 Definiendo los Campos Se pueden establecer los campos simplemente extendiendo el valor 'Translate' con otro array. ahora el modelo debería verse algo así: <?php class Post extends AppModel { var $name = 'Post'. Genial! De acuerdo con el ejemplo anterior. por ejemplo: <?php class Post extends AppModel { var $name = 'Post'. } ?> Luego de haber hecho esto (por ejemplo poner "nombre" como uno de los campos) ya has terminado la configuración básica. Más sobre esto en la siguiente sección. var $actsAs = array( 'Translate' => array( 'campoUno'.a no ser que ya se haya establecido. TranlateBehavior te permite invalidar esto último 'al vuelo'.6. lo cual permite al usuario de tus páginas crear multiples versiones sin la necesidad de que este cambie sus preferencias.language es establecido en la Clase L10n . Sin embargo. El valor de Config. [locale] => de_de ) [nameTranslation] => Array ( [0] => Array ( [id] => 1 [locale] => en_us [model] => Post [foreign_key] => 1 [field] => name [content] => Example entry ) [1] => Array ( [id] => 2 [locale] => de_de [model] => Post [foreign_key] => 1 [field] => name [content] => Beispiel Eintrag ) ) ) .5 Obtener todos los registros de traducción para un campo determinado Si se desea obtener todos los registros de traducción asociados al modelo actual. var $actsAs = array( 'Translate' => array( 'name' => 'nameTranslation' ) ).. simplemente se extiende el arreglo de campos en la configuracion como se muestra abajo. El nombre se puede definir sin restricciones <?php class Post extends AppModel { var $name = 'Post'.. } ?> Con esta configuración el resultado de find() se verá similar a esto: Array ( [Post] => Array ( [id] => 1 [name] => Beispiel Eintrag [body] => lorem ipsum.6.3. // need at least recursive 1 for this to work. where the key is the translatable field and the value is the fake association name. only when you need them. $this->Post->find('all'. [locale] => de_de ) [nameTranslation] => Array ( [0] => Array ( [id] => 1 [locale] => en_us [model] => Post [foreign_key] => 1 [field] => name [content] => Example entry ) [1] => Array ( [id] => 2 [locale] => de_de [model] => Post [foreign_key] => 1 [field] => name [content] => Beispiel Eintrag ) ) ) . $this->Post->bindTranslation(array ('name' => 'nameTranslation')).Nota: El registro del modelo contiene un campo virtual llamado "locale".3. $reset) $fields is a named-key array of field and association name. With this setup the result of your find() should look something like this: Array ( [Post] => Array ( [id] => 1 [name] => Beispiel Eintrag [body] => lorem ipsum... using the bindTranslation method bindTranslation($fields.5. el cual indica que "locale" es usado en el resultado. array ('recursive'=>1)). 6.1 Using the bindTranslation method You can also retrieve all translations. // we are going to save the german version $this->Post->create(). if ($this->Post->save($this->data)) { $this->redirect(array('action' => 'index')).3. var $actsAs = array( 'Translate' => array( 'name' ) ). You can do that either in your controller or you can define it directly in the model.6. .6 Saving in another language You can force the model which is using the TranslateBehavior to save in a language other than the on detected. To tell a model in what language the content is going to be you simply change the value of the $locale property on the model before you save the data to the database. } } } } ?> Example B: In your model <?php class Post extends AppModel { var $name = 'Post'. // Option 1) just define the property directly var $locale = 'en_us'. function add() { if ($this->data) { $this->Post->locale = 'de_de'. Example A: In your controller <?php class PostsController extends AppController { var $name = 'Posts'. To do so you need to setup your model like this: <?php class Post extends AppModel { var $name = 'Post'. } ?> Important is that you have to pluralize the table.3. var $actsAs = array( 'Translate' => array( 'name' ) ). // Use a different model (and table) var $translateModel = 'PostI18n'. There are two properties introduced by TranslateBehavior that allow to specify which "Model" to bind as the model containing the translations. The table schema itself must be identical with the one generated by the CakePHP console script. } } ?> 6. Lets say we want to save our translations for all posts in the table "post_i18ns" instead of the default "i18n" table. To make sure it fits one could just initialize a empty i18n table using the console and rename the table afterwards. These are $translateModel and $translateTable. .7 Multiple Translation Tables If you expect a lot entries you probably wonder how to deal with a rapidly growing database table. It is now a usual model and can be treated as such and thus comes with the conventions involved.// Option 2) create a simple method function setLanguage($locale) { $this->locale = $locale. But for better consistency we could do that in the model which actually uses this translation model.7. . This is where the optional $translateTable comes into play. } ?> Please note that you can't use $translateTable alone. // important } // filename: post_i18n.3. <?php class PostI18n extends AppModel { var $displayField = 'field'. like so: <?php class Post extends AppModel { var $name = 'Post'.3. You can also add all other model stuff here like $useTable.1 Create the TranslateModel For this to work you need to create the actual model file in your models folder. Reason is that it would break your setup and show you a "Missing Table" message for the default I18n model which is created in runtime. // Use a different table for translateModel var $translateTable = 'post_translations'. Make sure that you change the $displayField to 'field'. If you don't intend to use a custom $translateModel then leave this property untouched.6. Reason is that there is no property to set the displayField directly in the model using this behavior yet.2 Changing the Table If you want to change the name of the table you simply define $translateTable in your model. 6.php ?> That's all it takes. var $actsAs = array( 'Translate' => array( 'name' ) ).7. // Use a different model var $translateModel = 'PostI18n'. Sin embargo. parent_id INTEGER(10) DEFAULT NULL.). name VARCHAR(255) DEFAULT ''. Si esta familiarizado con la lógina MPTT ud puede preguntarse por que el campos parent existe .a menos que ud lo desee . Ejemplos de estos datos pueden ser: categorías con subcategorías ilimitadas. 6. rght INTEGER(10) DEFAULT NULL.2 Uso Básico El comportamiento de arbol tiene muchas cosas incluidas. `rght`) VALUES(1.tales como la búsquera de los hijos directos. para almacenar el valor lft de la fila actual. pero empecemos con un simple ejemplo . `parent_id`. lft INTEGER(10) DEFAULT NULL. para almacenar el valor rght de la fila actual.4. la tabla de la base de datos debe tener los 3 campos listados a continuación (enteros todos): • padre . • deracha . pero con un comportamiento de gran alcance que le permite utilizar los beneficios de la lógica MPTT sin preocuparse de cualquiera de los entresijos de la técnica .crearemos una pequeña tabla en una base de datos y le agregaremos algunos datos: CREATE TABLE categories ( id INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT. 6.4. 'Mis . INSERT INTO `categories` (`id`.4 Arboles (Tree) Es muy común que se desea almacenar datos jerárquicos en una tabla de base de datos. Cake Viene equipado con este paquete. PRIMARY KEY (id) ).el nombre por defecto del campo es lft. o cuando los datos son de sólo unos pocos niveles de profundidad es simple añadir un campo parent_id a su tabla de base de datos y utilizar este para hacer un seguimiento de cual item es padre de quien.1 Requerimientos Para poder usar la funcionalidad de árbol. Para pequeños árboles de datos.el nombre por defecto del campo es rght. `lft`.6. los datos relativos a un sistema de menús multinivel o una representación literal de la jerarquía tal como se utiliza para almacenar objetos de control de acceso con la lógica ACL. para almacenar el id del objeto padre • izquierda .simplemente es más facil de realizar ciertas tareas si existe un enlace directo al padre almacenado en la base de datos . `name`.el nombre por defecto del campo es parent_id. 29). Con un simple controlador: <?php class CategoriesController extends AppController { var $name = 'Categories'. VALUES(5. `lft`. 1. INSERT INTO `categories` (`id`. INSERT INTO `categories` (`id`. 'Diversion'. `name`. `parent_id`. `lft`. 4. 10. 10. INSERT INTO `categories` (`id`. 'Deportes'. 'Anual'. `lft`. podemos crear un metodo de testeo y obtener los contenidos de nuestro arbol de categorias para ver como queda. `rght`) VALUES(13. `parent_id`. 14). 17. 10. `name`. INSERT INTO `categories` (`id`. 2. VALUES(3. INSERT INTO `categories` (`id`. `name`. `name`. VALUES(6. 8). 'Gerald'. INSERT INTO `categories` (`id`. 'Gwendolyn'. `lft`. 'Surfing'. 18. INSERT INTO `categories` (`id`. 21). 2. 20. 15). 13). 6. `rght`) VALUES(12. INSERT INTO `categories` (`id`. 'Internacional'. VALUES(4. 9. `name`. `name`. `name`. VALUES(7. 6. `parent_id`. 3. `parent_id`. 16. `rght`) `rght`) `rght`) `rght`) `rght`) `rght`) `rght`) `rght`) VALUES(2. . 26. 7).Categorias'. 24. `rght`) VALUES(15. `lft`. 3. 'Nacional'. ' . `lft`. 'Viajes'. `parent_id`. `lft`. 1. `rght`) VALUES(11. 'Amigos'. 19). 23. VALUES(9. `lft`. `name`. `lft`. NULL. 9. 5). `parent_id`. `parent_id`. 2. INSERT INTO `categories` (`id`. `name`. `lft`. 28). `parent_id`. 30). 9. } } ?> y un metodo aun mas simple en el modelo: . INSERT INTO `categories` (`id`. 12. `name`. `parent_id`. die. . 6. `lft`. 'Alpinismo'. 22). 3. Para el propósito de verificar que todo está configurado correctamente. function index() { $this->data = $this->Category->generatetreelist(null.'). `parent_id`. `parent_id`. `parent_id`. null. 11). 'Status'. VALUES(8. INSERT INTO `categories` (`id`. `parent_id`. `lft`. null. `name`. INSERT INTO `categories` (`id`. 'Trabajo'. 13. `lft`. 27). `parent_id`. `rght`) VALUES(14. `name`. 25). `lft`. 1. 13. 'Reportes'. `name`. `rght`) VALUES(10. INSERT INTO `categories` (`id`. `name`. debug ($this->data). INSERT INTO `categories` (`id`. Por ejemplo: // pseudo código del controlador $data['Category']['parent_id'] = $data['Category']['name'] = $this->Category->save($data).<?php // app/models/category.1 Agregando datos En la seccion anterior.php class Category extends AppModel { var $name = 'Category'. y el comportamiento de arbol se encargara del resto. } ?> Podemos verificar como se ve que nuestro arbol de categoria visitando /categories Deberias ver algo como: • Mis Categorias • Diversion • Deportes • Surfing • Alpinismo • Amigos • Gerald • Gwendolyn • Trabajo • Reportes • Anual • Status • Viajes • Nacional • Internacional 6. Si no se setea el parent_id el comportamiento de arbol lo agregara al arbol como una entrada en el nivel superior: . usamos datos pre-existentes y chequeamos que se vieran en forma jerarquica con el método generatetreelist.2. 3. Sin embargo.4. 'Skating'. usualmente agregaríamos los datos de la misma forma que lo hariamos con cualquier modelo. var $actsAs = array('Tree'). Cuando se usa el comportamiento de arbol no es necesario hacer nada mas que configurar el parent_id. Ejecutando estos dos trozos de código alterará el árbol como sigue: • Mis Categorias • Diversion • Deportes • Surfing • Alpinismo • Skating New • Amigos • Gerald • Gwendolyn • Trabajo • Reportes • Anual • Status • Viajes • Nacional • Internacional • Otra Categoria New 6. Si modificas algo. tampoco lo hace la estructura de datos. Por ejemplo: // pseudo codigo de controlador $this->Category->id = 5. El codigo anterior no modifica el parent_id . $this->Category->save($data). el arbol de datos queda: • Mis Categorias • Diversion • Deportes • Surfing • Pesca Updated • Skating . pero no modificas el campo parent_id . si el valor no ha sido cambiado. Entonces. $data['Category']['name'] = 'Otra Categoria'.// pseudo codigo controlador $data = array().4.incluso si el parent_id es incluido en los datos que son pasados al método save.2 Modificando datos Modificar datos es tan transparente como agregar nuevos datos.la estructura de tus datos permanecera inalterada. // id de Apinismo $this->Category->save(array('name' =>'Pesca')).2. 4. Para eliminarla . diremos que la categoria reportes ya no es necesaria. array('name' => 'Otra Categoria')). Digamos que Pesca no pertenece a Deportes. // id of Pesca $newParentId = $this->Category->field('id'. Con el siguiente código: // pseudo codigo de controlador $this->Category->id = 5. $this->Category->save(array('parent_id' => $newParentId)).3 Borrando datos El comportamiento de arbol provee algunas formas para manejar la eliminación de datos.• Amigos • Gerald • Gwendolyn • Trabajo • Reportes • Anual • Status • Viajes • Nacional • Internacional • Otra Categoria Mover un dato a traves del arbol tambien es simple.2. Como es de esperar la estructura queda modificada a: • Mis Categorias • Diversion • Deportes • Surfing • Skating • Amigos • Gerald • Gwendolyn • Trabajo • Reportes • Anual • Status • Viajes • Nacional • Internacional • Otra Categoria • Pesca Movido 6. Para comenzar con un ejemplo simple. sino que deberia estar en Otra Categoria. 2. quizas obtengas resultados no deseados. El arbol de categorias sería modificado como sigue: • Mis Categorias • Diversion • Deportes • Surfing • Skating • Amigos • Gerald • Gwendolyn • Trabajo • Viajes • Nacional • Internacional • Otras Categorias • Pesca 6. con los arboles tenemos unos cuantos metodos más para su manipulacion.4.4. // un arreglo plano con 11 valores // -. El segundo parámetro es opcional y define si se entregan o no sólo los hijos directos. o llamas algun metodo de arboles entregandole un tipo de ordenamiento. Si llamas a find() y no ordenas por el campo lft.o bien -$this->Category->id = 1. $this->Category->delete().4 Haciendo consultas y usando tus datos Usar y manipular datos jerarquicos puede ser algo complejo.2. Usando el ejemplo de la sección anterior: $allChildren = $this->Category->children(1). por defecto en el roden en que aparecen en el árbol.y cualquier hijo que ella tenga basta llamar a delete tal como lo harias en cualquier modelo. Ademas de los metodos de busqueda del nucleo como find(). Por ejemplo. 6.4.1 El método children El método children toma la llave primaria (id) de una fila y retorna los hijos. Muchos metodos del comportamiento de arbol devuelven y se apoyan en el orden del campo lft. . en el siguiente código: // pseudo codigo de controlador $this->Category->id = 10. Usando los datos del ejemplo anterior: $totalChildren = $this->Category->childCount(1). $spacer= '_'.4. Below is an example of what you can expect this method to return. $keyPath=null. true).3 generatetreelist generatetreelist $recursive=null) ($conditions=null. // entrega 11 // -.Uses the same conditional options as find(). This method will return data similar to find('list').2.$allChildren = $this->Category->children(). //un arreglo plano con 2 valores Si quieres un arreglo recursivo utiliza find('threaded') 6. • $valuePath .2 Contando los hijos Tal como el método children. • $recursive . • $conditions . true).o bien -$this->Category->id = 1. $valuePath=null. with an indented prefix to show the structure of your data. with the following defaults: • $conditions = null • $keyPath = Model's primary key .The number of levels deep to fetch associated records All the parameters are optional.Path to the field to use for the key. $directChildren = $this->Category->childCount().4. El segundo parámetro es opcional y define si se contarán o no los hijos directos. // entrega 2 6.The string to use in front of each item to indicate depth. • $keyPath . // entrega 11 //Solo los hijos directos $numChildren = $this->Category->childCount(1.Path to the field to use for the label.4. • $spacer . // un arreglo plano con 11 valores // Retornar solo los hijos directos $directChildren = $this->Category->children(1.2. childCount toma la llave primaria (id) y retorna cuantos hijos tiene.4. 5 getpath getpath( $id = null..4. //<. For example: $parent = $this->Category->getparentnode(2).2. "___Surfing". "___Gwendolyn". "___Skating". So for example the path from the category "International" is: • My Categories • . "_Work".4.. return the parent node for any node. "Other People's Categories".. or false if the node has no parent (its the root node).4. as the name suggests. $recursive = null ) The 'path' when refering to hierachial data is how you get from where you are to the top. "___Gerald". $fields = null.4 getparentnode This convenience function will. "___International". "__Sport". "___National". "_Extreme fishing" 6.. • Work • Trips • . . Output: array( [1] => [2] => [3] => [4] => [16] => [6] => [7] => [8] => [9] => [13] => [14] => [15] => [17] => [5] => ) "My Categories". • International Using the id of "International" getpath will return each of the parents in turn (starting from the top).2. "__Trips". "__Friends".• $valuePath = Model's displayField • $spacer = '_' • $recursive = Model's recursive setting $treelist = $this->Category->generatetreelist().4. "_Fun".id for fun // $parent contains All categories 6. )). 'name' => 'My Categories'.3. 6.)). array('Category' => array('id' => 13.3 Uso Avanzado Este comportamiento de modelo no sólo trabaja en segundo plano. Todos los nodos hijos del nodo a mover también serán movidos Un ejemplo de una acción de controlador (en un controlador llamado Categories) que mueve un nodo hacia abajo del arbol: function movedown($name = null.4. Debes indicar el ID del elemento a mover y un número entero positivo de cuantas posiciones el nodo debería ser movido hacia abajo. hay una gran variedad de métodos específicos definidos en este comportamiento para antender todas tus necesidades de datos jerárquicos.4. y cualquier problema inesperado que pueda surgir en el proceso. $delta = null) { $cat = $this->Category->findByName($name). if ($delta > 0) { $this->Category->moveDown($this->Category->id.$parents = $this->Category->getpath(15). .. array('Category' => array('id' => 1.)).. ) array('Category' => array('id' => 9. . 'name' => 'International'. 'name' => 'Trips'. if (empty($cat)) { $this->Session->setFlash('No hay una categoría de nombre ' . array('Category' => array('id' => 15. } $this->Category->id = $cat['Category']['id']. // contents of $parents array( [0] => . true). null.1 moveDown Utilizado para mover un único nodo hacia abajo del árbol. } else { . 'name' => 'Work'. $name). $this->redirect(array('action' => 'index').. abs($delta)). 6.)).. [1] => [2] => [3] => . null. null.3. abs($delta)). } Por ejemplo. si quicieras mover la categoría "Sport" un nivel hacia abajo. } . } $this->Category->id = $cat['Category']['id']. $this->redirect(array('action' => 'index'). $name). null. } $this->redirect(array('action' => 'index'). if ($delta > 0) { $this->Category->moveup($this->Category->id. } else { $this->Session->setFlash('Please provide a number of positions the category should be moved up. $delta = null){ $cat = $this->Category->findByName($name). All child nodes will also be moved. true).4. } $this->redirect(array('action' => 'index'). 6.$this->Session->setFlash('Por favor indique el número de posiciones que el nodo debe ser movido hacia abajo.'). if (empty($cat)) { $this->Session->setFlash('There is no category named ' . Here's an example of a controller action (in a controller named Categories) that moves a node up the tree: function moveup($name = null. true). deberías llamar a: /categories/movedown/Sport/1. true).2 moveUp Used to move a single node up the tree. You need to provide the ID of the element to be moved and a positive number of how many positions the node should be moved up.'). 3. 6.3 removeFromTree removeFromTree($id=null. if you would like to move the category "Gwendolyn" up one position you would request /categories/moveup/Gwendolyn/1. Now the order of Friends will be Gwendolyn.true). and re-parenting all children. It offers more control than delete(). which will be reparented one level higher.4.For example. The Sport node will be become a top level node: • My Categories • Fun • Surfing • Extreme knitting • Skating • Sport Moved This demonstrates the default behavior of removeFromTree of moving the node to have no parent. Taking the following tree as a starting point: • My Categories • Fun • Sport • Surfing • Extreme knitting • Skating Running the following code with the id for 'Sport' $this->Node->removeFromTree($id). $delete=false) Using this method wil either delete or move a node but retain its sub-tree. which for a model using the tree behavior will remove the specified node and all of its children. Gerald. If however the following code snippet was used with the id for 'Sport' $this->Node->removeFromTree($id. The tree would become • My Categories • Fun • Surfing • Extreme knitting . default: $Model->displayField 'order' => .4.4 Data Integrity Debido a la naturaleza de las estructuras complejas autorreferentes como los árboles y las listas enlazadas.3. The opposite source of data will be populated based upon that source of info. //whether or not to verify the tree before reorder. 'order' => 'ASC'. ocasionalmente pueden romperse debido a una llamada poco cuidadosa. 6.g.4 reorder reorder ( array('id' => null. 6.4.4.• Skating This demonstrates the alternate use for removeFromTree. The missingParentAction parameter only applies to "parent" mode and determines what to do if the parent field contains an id that is not present.use the existing parent_id's to update the lft and rght fields . if the MPTT fields are corrupt or empty. $missingParentAction = null) The mode parameter is used to specify the source of info that is valid/correct. 'field' => $Model->displayField. default: 6. //direction to order. Available $mode options: • 'parent' . E. //which field to use in reordering. This method does not change the parent of any node. diseñadas para recuperarse de tales situaciones. 'verify' => true) ) Reorders the nodes (and child nodes) of the tree according to the field and direction specified in the parameters. ¡no todo está perdido! Tree Behavior contiene varias características que antes no estaban documentadas.1 Recover recover(&$model. default: 'ASC' 'verify' => )). $Model->id 'field' => . with the $mode 'parent' the values of the parent_id field will be used to populate the left and right fields. Tómalo con calma. the children have been reparented and 'Sport' has been deleted. $model->reorder(array( 'id' => . $mode = 'parent'. default: true //id of record to use as top node for reordering.4. 'DESC' for descending sort. • 'order' . • 'verify' .field to use for sorting. // or $this->Category->recover('parent').4.'ASC' for ascending. Reordering affects all nodes in the tree by default. $options is used to pass all extra parameters. // Rebuild all the parent_id's based on the lft and rght fields $this->Category->recover('tree').do nothing and carry on • 'return' .only reorder nodes below this node.4. however the following options can affect the process: • 'id' . $options = array()) Reorders the nodes (and child nodes) of the tree according to the field and direction specified in the parameters.2 Reorder reorder(&$model.delete the node • int . 'field' => $model->displayField. all of which are optional: array( 'id' => null.do nothing and return • 'delete' .set the parent_id to this id // Rebuild all the left and right fields based on the parent_id $this->Category->recover().whether or not to verify the tree prior to resorting. 'order' => 'ASC'.use the existing lft and rght fields to update parent_id Available missingParentActions options when using mode='parent': • null . 6. and has the following possible keys by default. default is the displayField for the model. • 'field' . This method does not change the parent of any node.• 'tree' . 'verify' => true . 4.) 6. Example output: Array ( [0] => Array ( [0] => [1] => [2] => ) [1] => Array ( [0] => [1] => [2] => ) [10] => Array ( [0] => [1] => [2] => ) [99] => Array ( [0] => [1] => [2] => ) ) node 3 left and right values identical node 2 The parent node 999 doesn't exist index 123 missing node 163 left greater than right . • 'message' depends on the error $this->Categories->verify().4. Each record in the output array is an array of the form (type.3 Verify verify(&$model) Returns true if the tree is valid otherwise an array of errors. incorrect index and message. id. with fields for type. message) • type is either 'index' or 'node' • 'id' is the id of the erroneous node. aculo. Contienen lógica de presentación que puede ser compartida por muchas vistas. Esta sección describe cada uno de los Helpers incluidos en CakePHP como Form.7 Ayudantes del Core Los Ayudantes o Helpers son clases a modo de componentes para la capa de presentación de tu aplicación. Para utilizar el AjaxHelper has de tener la versión actual de las librerías de JavaScript de www. CakePHP automáticamente aplicará el layout Ajax cuando se realize una petición de una acción mediante AJAX. Si se incluye el componente RequestHandler en el controlador.aculo.aculo. 7.us en la vista: echo $javascript->link('prototype').us en los layouts o vistas que requieran las funcionalidades de AjaxHelper. JavaScript y RSS. echo $javascript->link('scriptaculous').'Javascript').aculo. } Una vez que hayas incluido el helper de javascript en tu controlador. Ahora ya puedes puedes utilizar el helper Ajax en tu vista: $ajax->loquesea().1 AJAX El AjaxHelper utilizas las populares librerías Prototype y script.org y http://script. Html.us situadas en /app/webroot/js. Además has de incluir las librerías Prototype y script. elementos y layouts. Lee la sección Helpers para aprender más acerca de los helpers y cómo puedes crear los tuyos propios. Para poder cargar las librerías Prototype y script.us necesitarás incluir los helpers Ajax y Javascript en tu controlador: class WidgetsController extends AppController { var $name = 'Widgets'.aculo.'Ajax'. class WidgetsController extends AppController { . puedes utilizar el método link() de $javascript para incluir las liberías Prototype y script.prototypejs. var $helpers = array('Html'.us para operaciones Ajax y efectos en el lado del cliente. } 7. 7. Esta sección será de utilidad como referencia mientras inicias el uso de los métodos del AjaxHelper.2 Opciones de retrollamadas (Callback Options) Las opciones de Callbacks te permiten llamar a las funciones de JavaScript en puntos específicos en el proceso de solicitud (request).'Ajax'. Un uso común de esta es permitir ] la visibilidad de un indicador de progreso.1.'Javascript'). var $components = array( 'RequestHandler' ).var $name = 'Widgets'.1. Antes de cubrir los métodos específicos del helper. . o durante tus operaciones de AjaxHelper. $options['update'] El id del elemento DOM a ser actualizado con el contenido que retorne. $options['frequency'] El número de segundos entre intervalos de observación. $options['type'] Indica si la petición se realiza de forma síncrona ó asíncrona (por omisión). después. Puedes usar este arreglo para configurar el comportamiento del AjaxHelper. var $helpers = array('Html'. $options['before' Se ejecuta antes de que una solicitud sea efectuada. veamos las diferentes opciones disponibles a través de este arreglo.1.1 General Options $options['url'] La url del controlador/acción que se quiere llamar. usa estas Callbacks para estableces las cosas. 7.1 AjaxHelper Options La mayoría de los métodos del AjaxHelper permiten suministrar un arreglo de opciones ($options). Si estás buscando insertar un poco de lógica antes.1.1. $options keys Description $options['conditi El fragmento de código JavaScriptque necesita evaluar a true antes de que la solicitud on'] se inicie. array( 'update' => 'post' ) ). $options['compl Callback JavaScript a ser ejecutada cuando se completa un XMLHttpRequest. array $options. $options['loaded'Código de la Callback a ser ejecutado cuando un documento remoto es recivido por el ] cliente. array( 'controller' => 'posts'. $options['after'] JavaScript llamado inmediatamente después de que se ejecuta una solicitud. string $confirm.$options['confir Texto a mostrar en un cuadro de confirmación de JavaScript antes de proceder. boolean Devuelve un enlace a una acción remota definida por $options['url'] o $href que se llama en background utilizando XMLHttpRequest cuando se hace clic en el enlace.2. string $href. se dispare antes de que se ejecute la callback $options['loading'].1 link link(string $escapeTitle) $title.1. $options['interac Llamada cuando un usuario puede interactuar con el documento remoto. El resultado de esa petición puede ser insertada en un objeto DOM cuya identificación se puede especificar con $options['update'].1. Si $options['url'] esta en blanco href es utilizado en su lugar Ejemplo: <div id="post"> </div> <?php echo $ajax->link( 'View Post'. ete'] 7. m'] $options['loadin Código de la Callback que se ejecutará mientras que los datos se recuperan desde el g'] servidor.2 Métodos 7. ?> . a pesar de tive'] que no ha terminado de cargar. 1 ). 'action' => 'view'. array( 'controller' => 'posts'. 'complete' => 'alert( "Hello World" )' ). 'position' => 'top' ) ). ?> $confirm puede ser usado para llamar un JavaScript confirm() message antes de que la petición se efectúe. Ejemplo: <div id="post"> </div> <?php echo $ajax->link( 'Delete Post'. Permite al usuario prever la ejecución. array( 'update' => 'post' ). Para automatizar que el layout utilizado sea ajax incluir el componente RequestHandler en el controlador Por defecto el contenido del elemento es reemplazado. 'Do you want to delete this post?' ). array( 'controller' => 'posts'. 'action' => 'view'. Para cambiar este comportamiento especificar $options['position'] Ejemplo: <div id="post"> </div> <?php echo $ajax->link( 'View Post'. array( 'update' => 'post'. ?> . 1 ).Por defecto. 'action' => 'delete'. array( 'update' => 'post'. 1 ). 'action' => 'post'. estas solicitudes son procesadas asincrónicamente mientras se utilizan diferentes callbacks Ejemplo: <div id="post"> </div> <?php echo $ajax->link( 'View Post'. ?> ) Para usa procesamiento sincrónico especificar $options['type'] = 'synchronous'. 1). array( 'controller' => 'posts'. 2.2 remoteFunction remoteFunction(array $options). 'update' => 'post' ) ).7. 'action' => 'view'. Es usado principalmente como un helper(ayudante) para los enlaces(link). Esto no se utiliza muy a menudo a menos que usted necesite generar algunos codigos personalizados. the browser will ignore the server response. 'action' => 'view'. . 1 ). 1 ).1. Esta funcion crea el codigo JavaScript necesario para hacer una llamada remota. ?> </script> It can also be assigned to HTML Event Attributes: <?php $remoteFunction = $ajax->remoteFunction( array( 'url' => array( 'controller' => 'posts'. 'update' => 'post' ) ). ?> <div id="post" onmouseover="<?php echo $remoteFunction. ?>" > Mouse Over This </div> If $options['update'] is not passed. The $options for this function are the same as for the link method Example: <div id="post"> </div> <script type="text/javascript"> <?php echo $ajax->remoteFunction( array( 'url' => array( 'controller' => 'posts'. 1.4 form form(string $action. remoteTimer is the same as the remoteFunction except for the extra $options['frequency'] Example: <div id="post"> </div> <?php echo $ajax->remoteTimer( array( 'url' => array( 'controller' => 'posts'. Otherwise. every $options['frequency'] seconds. array $options) Returns a form tag that submits to $action using XMLHttpRequest instead of a normal HTTP request via $type ('post' or 'get'). it will be updated with the resulting document.2. The options array should include the model name e. string $type.'post'. 1 ). Usually used to update a specific div (specified by $options['update']) with the result of the remote call.g. If $options['update'] is specified.2. 'frequency' => 5 ) ). Plain Text View $ajax->form('edit'.1. 'position' => 'bottom'.3 remoteTimer remoteTimer(array $options) Periodically calls the action at $options['url']. Callbacks can be used. form submission will behave exactly like normal: data submitted is available at $this->data inside your controllers. ?> The default $options['frequency'] is 10 seconds 7.array('model'=>'User'. 'action' => 'view'. Callbacks can be used. . 'complete' => 'alert( "request completed" )'.7.'update'=>'UserInfoDiv')). 'update' => 'post'. . 'update'=>'UserInfoDiv'. 'url' => array( 'controller' => 'comments'.1. echo $form->input('name').5 submit submit(string $title.Alternatively. 'action'=>'add'). echo $ajax->submit('Submit'. array $options) Returns a submit button that submits the form to $options['url'] and updates the div specified in $options['update'] <div id='testdiv'> <?php echo $form->create('User'). echo $form->input('email'). echo $form->end(). i. if you need to cross post to another controller from your form: $ajax->form(array('type' => 'post'. You want the messages you specify in your validation rules to show up correctly. ?> </div> Use the $ajax->submit() method if you want form validation to work properly. 7. 'options' => array( 'model'=>'User'. If you want the form validation to work properly use the $ajax->submit() method as shown below.e.2. You should not use the $ajax->form() and $ajax->submit() in the same form. array('url'=> array('controller'=>'users'. 'update' => 'testdiv')). 'action' => 'edit' ) ) )). ?> observeField utiliza las mismas opciones que link El campo a enviar puede ser asignado utilizando $options['with']. array $options) Similar to observeField(). array( 'options' => $titles ) ) ?> </form> <?php echo $ajax->observeField( 'PostTitle'. Los datos enviados están disponibles en $this->data de tu controlador. The supplied $options are the same as observeField().2. but operates on an entire form identified by the DOM id $form_id. Por defecto este contiene Form. 'frequency' => 0. ?> <?php echo $form->input( 'title'.1. array( 'url' => array( 'action' => 'edit' ).6 observeField observeField(string $fieldId.2. except the default value of the $options['with'] option evaluates to the serialized (request string) value of the form. 3 => 'Harry' ). 2 => 'Dick'. Para enviar un formulario completo cuando el contenido cambie utilice $options['with'] = Form.7 observeForm observeForm(string $form_id. array $options) Observa el campo con el id DOM especificado por $field_id (cada $options['frequency'] segundos ) y realiza un XMLHttpRequest si su contenido ha cambiado. Los Callbacks pueden ser utilizados con esta función.serialize('$fieldId').2.serialize( $('Form ID') ) 7. ?> <?php $titles = array( 1 => 'Tom'. .1.Element.7. ) ). <?php echo $form->create( 'Post' ). } Next. '/posts/autoComplete')?> <?php echo $form->end('View Post')?> Once you've got the autoComplete() call working correctly. padding :0px.subject LIKE' => $this->data['Post'] ['subject']. you need to set up a controller action that fetches and organizes the data you'll need for your list. } li. } . $this->layout = 'ajax'. ?></li> <?php endforeach. utilize autoComplete() in a view to create your auto-completing form field: <?php echo $form->create('User'. You might end up using something similar to the following: div. array('url' => '/users/index')). array $options) Renders a text field with $fieldId with autocomplete.subject'. width :250px. background-color :white.2.8 autoComplete autoComplete(string $fieldId.'%' ). margin :0px. First. ?> <?php echo $ajax->autoComplete('Post. use CSS to style the auto-complete suggestion box. ?> </ul> Finally.7. array( 'conditions' => array( 'Post. string $url. based on user input: function autoComplete() { //Partial strings will come from the autocomplete field as //$this->data['Post']['subject'] $this->set('posts'.selected { background-color: #ffb. 'fields' => array('subject') ))). Often an unordered list is used for this. The remote action at $url should return a suitable list of autocomplete terms. $this->Post->find('all'. border :1px solid #888.auto_complete { position :absolute.1.ctp that uses that data and creates an unordered list in (X)HTML: <ul> <?php foreach($posts as $post): ?> <li><?php echo $post['Post']['subject']. create app/views/posts/auto_complete. drop(string $id. the element returns to its original position when the drags ends.9 isAjax isAjax() Allows you to check if the current request is a Prototype Ajax request inside a view. For more information on the parameters accepted in $options see http://github. array $options) Makes the DOM element specified by $id able to accept dropped elements. the droppable element will only react to a . The droppable element will only accept the dragged element if it is contained $options['containment'] in the given elements (element ids). Revert can also be an arbitrary function reference.com/madrobby/scriptaculous/wikis/droppables. Common options might include: $options keys Description Sets whether the element should only be draggable by an embedded handle. The first child/grandchild/etc. Common options might include: $options keys $options['accept'] Description Set to a string or javascript array of strings describing CSS classes that the droppable element will accept. If set to true. Can be a string or a javascript array of id $options['overlap'] references. If set to 'horizontal' or 'vertical'.7.2. The value must be an element reference or element id or a string referencing a CSS class value. For more information see http://github.2. array $options) Makes a Draggable element out of the DOM element specified by $id.1. Additional parameters can be specified with $options. Constrains the drag to either 'horizontal' or 'vertical'. leave blank for no $options['handle'] $options['revert'] $options['constraint'] constraints.10 drag & drop drag(string $id. element found within the element that has this CSS class value will be used as the handle. 7. Returns a boolean. The drop element will only accept elements of the specified CSS classes. Can be used for presentational logic to show/hide blocks of content.com/madrobby/scriptaculous/wikis/draggable. called when the drag ends.1. 2. $options['change'] JavaScript callback fired when the slider has finished moving. Defaults to horizontal $options['handleIma The id of the image that represents the handle. ] $options['slide'] JavaScript callback that is called whenever the slider is moved by dragging. This is used to swap out the image ge'] src with disabled image src when the slider is enabled. This is used to change the abled'] image src when the slider is disabled. 'horizontal' or 'vertical'. $options['handleDis The id of the image that represents the disabled handle. It $options['onSlide'] receives the slider's current value as a parameter. A javascript call back that is called when the dragged element is dropped on the droppable element. string $track_id. or has its value $options['onChange' changed. array $options) Makes a drop target that creates an XMLHttpRequest when a draggable element is dropped on it.draggable element if it is overlapping the droparea by more than 50% in the given axis.1.com/madrobby/scriptaculous/slider. Common options might include: $options keys $options['axis'] Description Sets the direction the slider will move in. Setting to 1 will make each pixel adjust ] the slider value by one. The $options array for this function are the same as those specified for drop() and link(). Used in conjunction with handleDisabled. Used in conjunction handleImage. For more information see http://wiki. array $options) Creates a directional slider control. 7. $options['increment' Sets the relationship of pixels to values. .11 slider $options['onDrop'] slider(string $id.github. dropRemote(string $id. The callback function receives the slider's current value as a parameter. For more information and demos see http://github.com/madrobby/scriptaculous/wikis/ajaxinplacecollectioneditor. value) $options['okText'] $options['cancelText'] $options['savingText'] $options['formId'] $options['externalControl'] $options['rows'] $options['cols'] $options['size'] $options['highlightcolor'] The row height of the input field The number of columns the text area should span Synonym for ‘cols’ when using single-line The highlight color Text of the submit button in edit mode The text of the link that cancels editing The text shown while the text is sent to the server $options['highlightendcolor'] The color which the highlight fades to $options['savingClassName'] $options['formClassName'] . $options['collection'] takes an array which is turned into options for the select. This can be used to format the information sent to the server.com/madrobby/scriptaculous/wikis/ajax-inplaceeditor. Common options might include: $options keys $options['collection'] Description Activate the 'collection' mode of in-place editing.7.2. $options['callback'] A function to execute before the request is sent to the server. The signature is function(form. array $options) Creates an in-place editor at DOM id. string $url.12 editor editor(string $id.1. To learn more about collection see http://github. The supplied $url should be an action that is responsible for saving element data. array() ). Either 'vertical' or 'horizontal'.1. $options['handle'] Makes the created Draggables use handles. the callback is called once on . $options['only'] $options['overlap'] $options['constraint'] Allows for further filtering of child elements.2. Defaults to vertical. Defaults to vertical. $options['onUpdate'] Called when the drag ends and the Sortable's order is changed in any way. 'action' => 'update_title'. When dragging from one Sortable to another. $id ).$options['loadingText'] $options['loadTextURL'] Example <div id="in_place_editor_id">Text To Edit</div> <?php echo $ajax->editor( "in_place_editor_id". Common options might include: $options keys $options['tag'] Description Indicates what kind of child elements of the container will be made sortable. Accepts a CSS class.com/madrobby/scriptaculous/sortable. accepts 'horizontal' or 'vertical'.13 sortable sortable(string $id. To find out more about sortable see http://wiki. Defaults to 'li'. The options array supports a number of parameters. array( 'controller' => 'Posts'. Restrict the movement of the draggable elements. array $options) Makes a list or group of floated objects contained by $id sortable. see the handle option on Draggables. ?> 7.github. Si CakePHP no encuentra una vista cacheada.2 Cache El helper Cache permite almacenar temporalmente (caching) layouts y vistas completas.2. Cualquier bloque no cacheado se procesa normalmente y la vista es entregada.2.2 Motores de Cache en Cake Lo nuevo en la version 1. Es importante notar que el helper de Cache opera de manera diferente a otros helpers. Muchas opciones para cada motor . $options['hoverclass'] Give the created droppable a hoverclass. CakePHP verifica si aquel requerimiento esta cacheado.Si lo está. o el cache ha expirado para la URL requerida. una vista es marcada con tags. Solo debes cachear elementos que puedan ser eventualmente regenerados si es necesario. 7. asi tambien menos código es ejecutado. No tiene metodos que se puedan llamar directamente. 7. ahorrando tiempo en descargar y retornar algunos datos.each Sortable. dragged elements of the sortable will be cloned and appear as a ghost. se continúa normalmente con el requerimiento. que permiten decirle cuales bloques no deben ser cacheados. El Caching no debe utilizarze nunca para almacenar datos en forma permanente.2 de CakePHP son varios motores de cache. La elección del motor de cache es controlada a traves del archivo app/config/core. Esto crea grandes ahorros en tiempos de proceso para cada requerimiento para una URL cacheada. Cuando una URL es requerida. $options['ghosting'] If set to true. Por ejemplo podrías almacenar el resultado de una consulta que requiere mucho tiempo de modo de no realizarla nuevamente en cada actualización de la página. El caching de vistas en CakePHP almacena temporalmente los layouts y vistas utilizando el motor de almacenamiento seleccionado. instead of directly manipulating the original element. 7.1 Generalidades en Caching El caching tiene como intención ser un medio de almacenamiento temporal que reduce la carga del servidor. En vez de eso. permitiendo almacenar vistas en multiples formas sin preocuparse de las caracteristicas especificas del medio de almacenamiento.php config. el resto del proceso es saltado. El helper de cache interactua transparentemente con estos motores. php.3 Configuracion del Cache Helper El caching de vistas y el helper de cache tienen varioes elementos de configuración importantes.2.4 Caching en los Controllers Cualquier controlador que utilice la funcionalidad de caching necesita incluir el CacheHelper en el arreglo $helpers. Si no se configura a true. debes primero configurar Configure::Cache. lo este método puede ser encontrada en php. entonces el cache no sera verificado o creado. El File Engine es el motor por defecto en CakePHP. pero implementanto el método XCache. Esto se hace a traves de la variable $cacheAction en tus controladores. var $helpers = array('Cache'). El motor Memcache funciona utilizando un servidor de almacenamiento en memoria. EL tiempo puede expresarse en formato strtotime(). El motor XCache opera en forma similar al APC.net y memcached APC XCache Memcache que permite crear objetos cache en la memoria del sistema. Usando como ejemplo ArticlesController. o "3 minutes"). y cuanto tiempo durará cacheada cada acción. Mas informacion acerca de 7. este motor almacena codigo ya compilado de PHP. $cacheAction debería ser configurada como un arreglo el cual contiene las acciones a ser cacheadas y la duracion en segundos que deben permanecer en tal condicion. (ie.2. Necesitas ademas indicar cuales acciones necesitan caching. Estos se detallan más abajo. Asi como XCache. pero funciona bien con los valores por defecto. "1 hour".de cache son listados en el archivo de configuracion core.php. Para usar el cache helper en cualquier vista o controlador. Requiere la autenticacion de usuarios para funcionar apropiadamente. 7. Escribe archivos planos en el sistema File de archivos y cuenta con numerosos parámetros.check a true en la linea 80 de core. EL motor APC implementa el método Alternative PHP Cache. mas detalles acerca de cada motor se puede encontrar en la seccion de Caching. que recibe un gran tráfico que necesita cachearse. . . certain parts of the page may look different whether a user is currently logged in or browsing your site as a guest. To indicate blocks of content that are not to be cached. <?php echo $session->read('User. 'view/52' ).2. 7. the controller method for the action will not be called .name')) : ?> Welcome.otherwise what would be the point of caching the page. Therefore.Por ejemplo. 'users/login')?> <?php endif. <?php else: ?> <?php echo $html->link('Login'. => 48000 Hacer caching de una acción completa en este caso un listado de articulos var $cacheAction = array( 'archives/' => '60000' ).name')?>. Cachear todas las acciones del controlador usando un formato amigable strtotime() para indicar el tiempo de cacheo. ?> </cake:nocache> It should be noted that once an action is cached. var $cacheAction = "1 hour". it is not possible to wrap <cake:nocache> </cake:nocache> around variables which are set from the controller as they will be null.5 Marking Non-Cached Content in Views There will be times when you don't want an entire view cached. 'view/48/' => 36000. For example. wrap them in <cake:nocache> </cake:nocache> like so: <cake:nocache> <?php if ($session->check('User. cachear los articulos visitados frecuentemente por diversos periodos de tiempo var $cacheAction = array( 'view/23/' => 21600. o si lo desea puede usar métodos específicos para hacer solo lo que necesite. La mayor parte del trabajo de creación de formularios recae sobre el uso de esta nueva clase. Si fuera a llamar create() dentro de una vista de UsersController. 7. and there has been an INSERT. This will clear all cached data. use clearCache(). or DELETE query made to a Post. if a cached view uses data from the Post model. create(string $modelo = null. If you need to clear the cached view files. El método por omisión para el envío es POST. de esta manera agiliza la validación. UPDATE.este hará casi todo automáticamente por usted.7. Si create() es llamado sin parámetros. 7.2.1 Creando Formularios El primer método que necesitarás para poder aprovecha el FormHelper es create().3. El ID es generado usando el nombre del modelo y el nombre de la acción del controlador en formato CamelCased. excluding cached view files. el precargado y el diseño de la interfaz.6 Clearing the Cache It is important to remember that the Cake will clear a cached view if a model used in the cached view is modified. you can do so by calling Cache::clear(). array $opciones = array()) Todos los parámetros son opcionales. El FormHelper se centra en la creación de formularios rápidamente. For example. El elemento form es regresado con un ID DOM. If you need to manually clear the cache. El FormHelper es bastante flexible . Esto pondrá los datos de tu formulario en el array: $this->data (en lugar de ponerlos en en sub-array: $this- . asume que estás construyendo un formulario que será enviado al controlador actual. en lugar de los (ahora obsoletos) métodos del HtmlHelper. and new content is generated on the next request. vería algo como lo siguiente en la vista <form id="UserAddForm" method="post" action="/users/add"> Puedes también pasar false para el parámetro $modelo. the cache for that view is cleared. Este método se encarga de escribir la etiqueta de apertura del formulario. ya sea vía la acción add() o edit().3 Formularios El FormHelper es una nueva adición a CakePHP. and that array contains a non-empty value of the model's primary key. you are creating that form's context. and all models referenced are assumed to be associated with it. you can also use the same logic to create an edit form. } else { // Save logic goes here } } ?> // views/recipes/edit. By specifying a model for a form. however. First.php: <?php function edit($id = null) { if (empty($this->data)) { $this->data = $this->Recipe->findById($id). However. . then it assumes you are using the default model for the current controller. ?> //Output: <form id="RecipeEditForm" method="post" action="/recipes/edit/5"> <input type="hidden" name="_method" value="PUT" /> Since this is an edit form. The FormHelper uses the $this->data property to automatically detect whether to create an add or edit form. Esto puede ser muy útil para formularios cortos que quizá no representen nada en tu base de datos. This special array can contain a number of different key-value pairs that affect the way the form tag is generated. ?> //Output: <form id="RecipeAddForm" method="post" action="/recipes/add"> This will POST the form data to the add() action of RecipesController.>data['Model']). we should get an edit form <?php echo $form->create('Recipe'). If you do not specify a model. if we browse to http://site. a hidden input field is generated to override the default HTTP method. you can specify a model name. All fields are assumed to belong to this model (unless otherwise specified). For example. <?php echo $form->create('Recipe').ctp: // Since $this->data['Recipe']['id'] = 5. then the FormHelper will create an edit form for that record. The $options array is where most of the form configuration happens. The create() method allows us to customize much more using the parameters. we might get the following: // controllers/recipes_controller.com/recipes/edit/5. If $this->data contains an array element named after the form's model. The absence of the proper enctype attribute will cause the file uploads not to function. array('type' => 'file')). if you’d like to point the form to the login() action of the current controller.2 $options[‘action’] The action key allows you to point the form to a specific action in your current controller. ?> //Output: <form id="UserAddForm" enctype="multipart/form-data" method="post" action="/users/add"> When using ‘put’ or ‘delete’.3. <?php echo $this->Form->create('User'.7. and includes an enctype of “multipart/form-data” on the form tag. respectively. ?> //Output: <form id="UserAddForm" method="get" action="/users/add"> Specifying ‘file’ changes the form submission method to ‘post’. array('action' => 'login')). ‘get’. array('type' => 'get')). This allows CakePHP to emulate proper REST support in web browsers. For example. 7. This is to be used if there are any file elements inside the form. ‘put’ and ‘delete’. but when submitted.1. your form will be functionally equivalent to a 'post' form. ?> //Output: <form id="UserLoginForm" method="post" action="/users/login"> </form> . <?php echo $this->Form->create('User'. ‘file’. you would supply an $options array like the following: <?php echo $this->Form->create('User'. the HTTP request method will be overridden with 'PUT' or 'DELETE'.3. Supplying either ‘post’ or ‘get’ changes the form submission method accordingly. Valid values include ‘post’.1.1 $options[‘type’] This key is used to specify the type of form to be created. 7.5 $options['inputDefaults'] You can declare a set of default options for input() with the inputDefaults key to customize your default input creation. 'action' => 'add'))). the form’s submit action is changed so that pressing the submit button does not submit the form. ?> // or <?php echo $this->Form->create(null.1. <?php echo $this->Form->create(null.1.3 $options[‘url’] If the desired form action isn’t in the current controller. ?> //Output: <form method="post" action="/recipes/add"> <?php echo $this->Form->create(null.7. 7. If the form is meant to be submitted via AJAX.5 7.3. array('url' => array('controller' => 'recipes'. The supplied URL can be relative to your CakePHP application.1.com/search'.4 $options[‘default’] If ‘default’ has been set to boolean false. array('url' => '/recipes/add')). array( 'inputDefaults' => array( 'label' => false.com/search"> Also check HtmlHelper::url method for more examples of different types of urls.google. 'type' => 'get' )).3. setting ‘default’ to false suppresses the form’s default behavior so you can grab the data and submit it via AJAX instead. . or can point to an external domain. array( 'url' => 'http://www. 'div' => false ) )). you can specify a URL for the form action using the ‘url’ key of the $options array. echo $this->Form->create('User'. ?> //Output: <form method="get" action="http://www.google.3.3.1. // No div.3. <?php echo $form->create(). A menudo. array('label' => 'Username')). el FormHelper agregará un boton submit llamado de esa manera además de la etiqueta de cierre del formulario. ?> Output: <div class="submit"> <input type="submit" value="Finish" /> </div> </form> 7. // has a label element 7.3 Automagic Form Elementos Primero. El Método principal que veremos es input().All inputs created from that point forward would inherit the options declared in inputDefaults. demos una mirada a algunos de los métodos de creción más automáticos en el FormHelper. pero el usar end() también hace que el FormHelper inserte los elementos hidden necesarios en el formulario para los métodos que dependen de este. You can override the defaultOptions by declaring the option in the input() call. ?> <!-. . ?> Si una cadena es colocada como primer parámetro del end(). Este método automaticamente inspecciona el modelo del campo que ha sido proporcionado a fin de crear un elemento de entrada apropiado para ese campo. no label echo $this->Form->input('username'. <?php echo $form->end('Finish').2 Closing the Form El FormHelper tambien incluye un método end() que completa el código del formulario. echo $this->Form->input('password').Form elements go here --> <?php echo $form->end().3. el método end() solo escribe la etiqueta de cierre del formulario. echo $form->input('password').18 ) ). month. 'maxYear' => date('Y') . ?> <?php echo $form->end('Add'). here's an example for creating a hasAndBelongsToMany select. echo $form->input('quote'). Besides the specific input options found below you can specify any html attribute (for instance onfocus). year. varchar. I can use the input() method of the FormHelper to create appropriate inputs for all of these form fields. and meridian selects For example. etc. 'minYear' => date('Y') . In the controller action you would put the following: $this->set('groups'.) text boolean. month.70 . minute. 'dateFormat' => 'DMY' . year. . passwd. hour. For more information on $options and $htmlAttributes see HTML Helper. and meridian selects time hour. tinyint(1) checkbox text textarea text. $this->User->Group->find('list')). or ExtraFunkyModel -> extraFunkyModels) with the select options. meridian //textarea A more extensive example showing some options for a date field: echo $form->input('birth_dt'. approved (datetime) and quote (text). and year selects datetime. minute. array( 'label' => 'Date of birth' . array $options = array()) Column Type Resulting Form Field string (char. ?> <?php echo $form->input('username'). And to round off. In your controller. or psword password date day. <?php echo $form->create(). echo $form->input('approved'). with name of password. Assume that User hasAndBelongsToMany Group. timestamp day. month. ?> //text //password //day. set a camelCase plural variable (group -> groups in this case. hour. minute. password (varchar).input(string $fieldName. let’s assume that my User model includes fields for a username (varchar). fieldname'). "UserGroup". $this->ReallyInappropriateModelName>find('list')). If your model name consists of two or more words. $this->User->Group->find('list')). e.. use the following convention: <?php echo $this->Form->input('Modelname. // or $this->set('reallyInappropriateModelNames'. ?> <input type="text" id="Modelname0Fieldname" name="data[Modelname][0][fieldname]"> <input type="text" id="Modelname1Fieldname" name="data[Modelname][1][fieldname]"> . $this->UserGroup->find('list')). 7.fieldname').fieldname as the first parameter. thus creating an array that can be saved in one shot with saveAll(). echo $this->Form->input('Modelname. Afterwards.g.3. you can add the following to your Users-controller (assuming your User belongsTo Group): $this->set('groups'.And in the view a multiple select can be expected with this simple code: echo $form->input('Group').1 Field naming convention The Form helper is pretty smart.3. echo $this->Form->input('Modelname. Whenever you specify a field name with the form helper methods. when passing the data using set() you should name your data in a pluralised and camelCased format as follows: $this->set('userGroups'. it'll automatically use the current model name to build an input with a format like the following: <input type="text" id="ModelnameFieldname" name="data[Modelname][fieldname]"> You can manually specify the model name by passing in Modelname.or hasOne-Relation.0. If you want to create a select field while using a belongsTo.1. If you need to specify multiple fields using the same field name.fieldname'). add the following to your form-view: echo $form->input('group_id'). <?php echo $this->Form->input('field'. ?> Output: <div class="input"> <label for="UserField">Field</label> <input type="file" name="data[User][field]" value="" id="UserField" /> </div> 7. <?php echo $this->Form->input('field'. 'after' => '--after--'. you can also create ‘file’. array( 'before' => '--before--'. and ‘password’ inputs.7.3. array( 'before' => '--before--'. array('type' => 'file')). 'between' => '--between---' )). <?php echo $this->Form->input('field'.3 $options[‘before’]. $options[‘between’]. . 'after' => '--after--'. In addition to the field types found in the table above.3.3.2 $options[‘type’] You can force the type of an input (and override model introspection) by specifying a type. 'between' => '--between---'.?> Output: <div class="input"> --before-<label for="UserField">Field</label> --between--<input name="data[User][field]" type="text" value="" id="UserField" /> --after-</div> For radio type input the 'separator' attribute can be used to inject markup to separate each input/label pair. $options[‘separator’] and $options[‘after’] Use these keys if you need to inject some markup inside the output of the input() method.3. ?> Output: <div class="input"> <label for="UserField">Field</label> <select name="data[User][field]" id="UserField"> <option value="0">1</option> <option value="1">2</option> <option value="2">3</option> <option value="3">4</option> <option value="4">5</option> </select> </div> .4 $options[‘options’] This key allows you to manually specify options for a select input.2. Unless the ‘type’ is specified as ‘radio’. Defaults to '-'. '2') )). 'options' => array('1'.3.3. or for a radio group. 7.?> Output: <div class="input"> --before-<input name="data[User][field]" type="radio" value="1" id="UserField1" /> <label for="UserField1">1</label> --separator-<input name="data[User][field]" type="radio" value="2" id="UserField2" /> <label for="UserField2">2</label> --between----after-</div> For date and datetime type elements the 'separator' attribute can be used to change the string between select elements.'separator' => '--separator--'. the FormHelper will assume that the target output is a select input.5))). array('options' => array(1.4. <?php echo $this->Form->input('field'.3. <?php echo $this->Form->input('field'. ?> Output: <div class="input"> <label for="UserField">Field</label> <select name="data[User][field]" id="UserField"> <optgroup label="Label1"> <option value="Value 1">Label 1</option> <option value="Value 2">Label 2</option> </optgroup> <optgroup label="Label2"> <option value="Value 3">Label 3</option> </optgroup> </select> </div> . ?> Output: <div class="input"> <label for="UserField">Field</label> <select name="data[User][field]" id="UserField"> <option value="Value 1">Label 1</option> <option value="Value 2">Label 2</option> <option value="Value 3">Label 3</option> </select> </div> If you would like to generate a select with optgroups. but instead of optgroups wraps elements in fieldsets. array('options' => array( 'Label1' => array( 'Value 1'=>'Label 1'. Works on multiple checkboxes and radio buttons too. 'Value 2'=>'Label 2' ). array('options' => array( 'Value 1'=>'Label 1'. 'Value 3'=>'Label 3' ))).Options can also be supplied as key-value pairs. <?php echo $this->Form->input('field'. just pass data in hierarchical format. 'Label2' => array( 'Value 3'=>'Label 3' ) ))). 'Value 2'=>'Label 2'. array('div' => 'class_name')).3. Using a string value will set the div's class name.7 $options[‘div’] Use this option to set attributes of the input's containing div. array( 'type' => 'select'.6 $options[‘maxLength’] Defines the maximum number of characters allowed in a text input. An array will set the div's attributes to those specified by the array's keys/values. 'multiple' => 'checkbox' )). $form->input('Modelo.3. array( 'type' => 'select'. Output: <div class="input text" id="mainDiv" title="Div Title" style="display:block"> <label for="UserName">Name</label> <input name="data[User][name]" type="text" value="" id="UserName" /> </div> . 7. poniendo ‘multiple’ igual a ‘checkbox’ la salida será una lista de checkboxes relacionados. Output: <div class="class_name"> <label for="UserName">Name</label> <input name="data[User][name]" type="text" value="" id="UserName" /> </div> Setting multiple attributes: echo $this->Form->input('User.campo'.5 $options[‘multiple’] Si ‘multiple’ es puesto a true para una entrada de tipo select. array('div' => array('id' => 'mainDiv'. Alternatively.name'.3. 'title' => 'Div Title'.7.name'. el select admitirá multiples selecciones.campo'.3. 'multiple' => true )). Alternativamente.3. 7. $form->input('Modelo.3. Setting the class name: echo $this->Form->input('User. you can set this key to false to disable the output of the div. 'style' => 'display:block'))). 3. array( 'label' => array('class' => 'thingy'. you can use a text key in the array to customize the label text. 'text' => 'The User Alias') ) ). set this key to false to disable the output of the label. array('div' => false)).Disabling div output: <?php echo $this->Form->input('User.name'. <?php echo $this->Form->input( 'User.?> Output: <label for="UserName">Name</label> <input name="data[User][name]" type="text" value="" id="UserName" /> 7.?> Output: <div class="input"> <label for="UserName">The User Alias</label> <input name="data[User][name]" type="text" value="" id="UserName" /> </div> Alternatively.name'.3. ?> Output: <div class="input"> <input name="data[User][name]" type="text" value="" id="UserName" /> </div> Set this to an array to provide additional options for the label element.8 $options[‘label’] Set this key to the string you would like to be displayed within the label that usually accompanies the input.name'. ?> Output: <div class="input"> <label for="UserName" class="thingy">The User Alias</label> <input name="data[User][name]" type="text" value="" id="UserName" /> </div> . <?php echo $this->Form->input( 'User.name'. If you do this. array( 'label' => false ) ). array( 'label' => 'The User Alias' ) ). <?php echo $this->Form->input( 'User. 7. $this->Form->input('Model. array('error' => array('tooShort' => __('This is not long enough'. array('error' => array('escape' => false))). use the following format: $this->Form->input('Model. true) ))). .3. wrapping element class name.9 $options['legend'] Some inputs like radio buttons will be automatically wrapped in a fieldset with a legend title derived from the fields name. and whether HTML in the error message will be escaped. array('error' => false)). The title can be overridden with this option. Setting this option to false will completely eliminate the fieldset. As seen above you can set the error message for each validation rule you have in your models.field'.3. array('error' => array('wrap' => 'span'.field'.7. To modify the wrapping element type and its class. In addition you can provide i18n messages for your forms. set the escape suboption to false: $this->Form->input('Model.3. To override the model error messages use an associate array with the keyname of the validation rule: $this->Form->input('Model.3. for example.field'. To disable error message output set the error key to false.3. It has a number of suboptions which control the wrapping element.field'.11 $options['error'] Using this key allows you to override the default model error messages and can be used. 'class' => 'bzzz'))). To prevent HTML being automatically escaped in the error message output.10 $options[‘id’] Set this key to force the value of the DOM id for the input.3. 7. to set i18n messages. ?> You cannot use default to check a checkbox . time.3. 'selected' => '13:30:00')).14 $options[‘rows’]. array('rows' => '5'.e.3. array('default'=>'Sugar')).3. The value is used if the data passed to the form does not contain a value for the field (or if no data is passed at all). echo $this->Form->input('close_time'. ?> Example with select field (Size "Medium" will be selected as default): <?php $sizes = array('s'=>'Small'. 'default'=>'m')). 'cols' => '5')).12 $options['default'] Used to set a default value for the input field. Set ‘selected’ to the value of the item you wish to be selected by default when the input is rendered. 7. For types select. date.instead you might set the value in $this>data in your controller. 7. 'l'=>'Large'). or set the input option checked to true. echo $this->Form->input('textarea'. array('type' => 'time'. Output: <div class="input text"> <label for="FormTextarea">Textarea</label> .3. $options[‘cols’] These two keys specify the number of rows and columns in a textarea input. datetime). Date and datetime fields' default values can be set by using the 'selected' key. array('options'=>$sizes. $this->Form->data in your view. echo $this->Form->input('size'. 'm'=>'Medium'.7. Example usage: <?php echo $this->Form->input('ingredient'.3.13 $options[‘selected’] Used in combination with a select-type input (i. The selected key for date and datetime inputs may also be a UNIX timestamp.3. ‘24’.3.3.15 $options[‘empty’] If set to true. If you want to have a empty value with text displayed instead of just a blank option. <?php echo $this->Form->input('field'. pass in a string to empty.4.2.<textarea name="data[Form][textarea]" cols="5" rows="5" id="FormTextarea" > </textarea> </div> 7. this creates a blank option with an empty value in your drop down list. When passed to a select list.5). Options can also supplied as key-value pairs. .3. forces the input to remain empty. 'empty' => '(choose one)')). use 'value' => '' instead. ?> Output: <div class="input"> <label for="UserField">Field</label> <select name="data[User][field]" id="UserField"> <option value="">(choose one)</option> <option value="0">1</option> <option value="1">2</option> <option value="2">3</option> <option value="3">4</option> <option value="4">5</option> </select> </div> If you need to set the default value in a password field to blank. and ‘none’. Valid values include ‘12’. 7. array('options' => array(1.3.3.16 $options[‘timeFormat’] Used to specify the format of the select inputs for a time-related set of inputs. 17 $options[‘dateFormat’] Used to specify the format of the select inputs for a date-related set of inputs.4 File Fields To add a file upload field to a form. 7. you must first make sure that the form enctype is set to "multipart/form-data". ?> Would create 4 options in the minute select.submittedfile'). array('class' => 'custom-class')). so start off with a create function such as the following.time'. ‘MDY’. and ‘NONE’. // OR echo $this->Form->create('Document'.3. 7. echo $this->Form->create('Document'. ‘YMD’.3. Defines the lower and/or upper end of values shown in the years select field.3. 'interval' => 15)). <?php echo $this->Form->input('Model. array('between'=>'<br />'.3.18 $options['minYear']. Next add either of the two lines to your form view file. array('type' => 'file')). $options['maxYear'] Used in combination with a date/datetime input.3.19 $options['interval'] This option specifies the number of minutes between each option in the minutes select box.20 $options['class'] You can set the classname for an input field using $options['class'] echo $this->Form->input('title'. // or echo $this->Form->file('Document. array('enctype' => 'multipart/form-data') ). Valid values include ‘DMY’. 7.'type'=>'file')).3.3. One for each 15 minutes.3.3. array('type' => 'time'.7.submittedfile'. echo $this->Form->input('Document. 7. . 1 Validating Uploads Below is an example validation method you could define in your model to validate whether a file has been successfully uploaded. Upon submission. // Based on comment 8 from: http://bakery. file fields provide an expanded data array to the script receiving the form data. the values in the submitted data array would be organized as follows.org/articles/view/improved- advance-validation-with-parameters function isUploadedFile($params){ $val = array_shift($params). } return false.cakephp. it is not possible to put default values into input fields of type 'file'. This array is generated by PHP itself. if ((isset($val['error']) && $val['error'] == 0) || (!empty( $val['tmp_name']) && $val['tmp_name'] != 'none')) { return is_uploaded_file($val['tmp_name']). Each time the form is displayed.pdf 'type' => application/pdf 'tmp_name' => C:/WINDOWS/TEMP/php1EE. 7. the value inside will be empty. } . so for more detail on the way PHP handles data passed via file fields read the PHP manual section on file uploads.Due to the limitations of HTML itself.4.tmp 'error' => 0 'size' => 41737 ). $this->data['Document']['submittedfile'] = array( 'name' => conference_schedule. 'tmp_name' will have a different path in a Unix environment.3. if the CakePHP was installed on a Windows server. For the example above. array('class' => 'users')). 3.3. <?php echo $this->Form->checkbox('done').(the default). $options is used primarily to specify HTML tag attributes (such as the value or DOM id of an element in the form).5.3. 2. Many of these methods also make use of a special $options parameter. <?php echo $this->Form->button('A Button').5. array('type'=>'reset')).7. In this case.3. ?> .1 checkbox checkbox(string $fieldName. ?> Will output: <input type="hidden" name="data[User][done]" value="0" id="UserDone_" /> <input type="checkbox" name="data[User][done]" value="1" id="UserDone" /> 7. <?php echo $this->Form->text('username'. echo $this->Form->button('Submit Form'. array $options) Creates a checkbox form element. This method also generates an associated hidden form input to force the submission of data for the specified field. button: Creates a standard push button. array('type'=>'submit')). Setting $options['type'] will output one of the three possible button types: 1. array $options = array()) Creates an HTML button with the specified title and a default type of "button". ?> Will output: <input name="data[User][username]" type="text" class="users" id="UserUsername" /> 7. reset: Creates a form reset button. echo $this->Form->button('Reset the Form'.5 Form Element-Specific Methods The rest of the methods available in the FormHelper are for creating specific form elements. array('type'=>'button')). submit: Same as the $this->Form->submit method . however. echo $this->Form->button('Another Button'.2 button button(string $title. the select will not include an empty option. array('type'=>'submit'. <?php echo $this->Form->year('purchased'. $attributes) int $maxYear. <?php echo $this->Form->button('Submit Form'.5. with the $selected year selected by default.date('Y')). int $minYear. Defaults to false. HTML attributes may be supplied in $attributes.Will output: <button type="submit">A Button</button> <button type="button">Another Button</button> <button type="reset">Reset the Form</button> <button type="submit">Submit Form</button> The button input type allows for a special $option attribute called 'escape' which accepts a bool and determines whether to HTML entity encode the $title of the button.3 year year(string $fieldName. mixed $selected. ?> 7.'escape'=>true)). array Creates a select element populated with the years from $minYear to $maxYear. If $attributes['empty'] is false.2000.3. ?> Will output: <select name="data[User][purchased][year]" id="UserPurchasedYear"> <option value=""></option> <option value="2009">2009</option> <option value="2008">2008</option> <option value="2007">2007</option> <option value="2006">2006</option> <option value="2005">2005</option> <option value="2004">2004</option> <option value="2003">2003</option> <option value="2002">2002</option> <option value="2001">2001</option> <option value="2000">2000</option> </select> . ?> Will output: <select name="data[User][mob][month]" id="UserMobMonth"> <option value=""></option> <option value="01">January</option> <option value="02">February</option> <option value="03">March</option> <option value="04">April</option> <option value="05">May</option> <option value="06">June</option> <option value="07">July</option> <option value="08">August</option> <option value="09">September</option> <option value="10">October</option> <option value="11">November</option> <option value="12">December</option> </select> You can pass in your own array of months to be used by setting the 'monthNames' attribute.3. mixed $selected.5. null. (Note: the default months are internationalized and can be translated using localization. array('monthNames' => false)). or have months displayed as numbers by passing false.4 month month(string $fieldName. boolean $showEmpty) Creates a select element populated with month names.) <?php echo $this->Form->month('mob'. array $attributes. ?> .7. <?php echo $this->Form->month('mob'). 7. Valid values for $timeFormat are ‘12’. array $attributes. you can supply the text as the final parameter as follows: <?php echo $this->Form->day('created'). You can specify not to display empty values by setting "array('empty' => false)" in the attributes parameter. the first option is 'Day').. $timeFormat = '12'. ‘YMD’ or ‘NONE’.6 day day(string $fieldName. You also can pre-select the current datetime by setting $selected = null and $attributes = array("empty" => false). $dateFormat = 'DMY'.g. boolean $showEmpty) Creates a select element populated with the (numerical) days of the month.5. $selected = null. mixed $selected. $attributes = array()) Creates a set of select inputs for date and time.3. and null. ?> Will output: <select name="data[User][created][day]" id="UserCreatedDay"> <option value=""></option> <option value="01">1</option> <option value="02">2</option> <option value="03">3</option> . To create an empty option with prompt text of your choosing (e. <option value="31">31</option> </select> . Valid values for $dateformat are ‘DMY’. ‘MDY’.3.5 dateTime dateTime($fieldName.5.7.. ‘24’. • 'wrap' mixed Whether or not the error message should be wrapped in a div. array $attributes. specified by $text. If a string. for the given field.3. echo $this->Form->file('avatar'). in the event that a validation error has occurred. <?php echo $this->Form->create('User'. mixed $text.10 error error(string $fieldName.8 minute minute(string $fieldName.11 file file(string $fieldName.5. boolean Creates a select element populated with ‘am’ and ‘pm’.3. 7. array $attributes. array $options) Creates a file input. boolean $format24Hours.7 hour hour(string $fieldName. ?> . mixed $selected. • 'class' string The classname for the error message 7.3.7. 7. array $options) Shows a validation error message. will be used as the HTML tag to use.9 meridian meridian(string $showEmpty) $fieldName.5. mixed $selected.3. 7. boolean $showEmpty) Creates a select element populated with the minutes of the hour.5. array $attributes. mixed $selected. Options: • 'escape' bool Whether or not to html escape the contents of the error.5.5.array('type'=>'file')). boolean $showEmpty) Creates a select element populated with the hours of the day.3. Example: <?php echo $this->Form->hidden('id').13 isFieldError isFieldError(string $fieldName) Returns true if the supplied $fieldName has an active validation error. 7.Will output: <form enctype="multipart/form-data" method="post" action="/users/add"> <input name="data[User][avatar]" value="" id="UserAvatar" type="file"> When using $this->Form->file().3. string $text. remember to set the form encoding-type. errors are rendered by default. populated with $text.3. by setting the type option to 'file' in $this->Form->create() 7.3. } ?> When using $this->Form->input().12 hidden hidden(string $fieldName. ?> Will output: <label for="UserStatus">Status</label> . array $attributes) Creates a label tag. ?> Will output: <input name="data[User][id]" value="10" id="UserId" type="hidden"> 7.5. <?php echo $this->Form->label('status').14 label label(string $fieldName.5.5. array $options) Creates a hidden form input. <?php if ($this->Form->isFieldError('gender')){ echo $this->Form->error('gender'). Use $attributes['value'] to set which value should be selected default. <?php $options=array('M'=>'Male'.16 radio radio(string $fieldName.$options.5. . <br />).3. $attributes=array('legend'=>false).$attributes). array $options) Creates a password field.15 password password(string $fieldName. array $options. ?> Will output: <input name="data[User][gender]" id="UserGender_" value="" type="hidden"> <input name="data[User][gender]" id="UserGenderM" value="M" type="radio"> <label for="UserGenderM">Male</label> <input name="data[User][gender]" id="UserGenderF" value="F" type="radio"> <label for="UserGenderF">Female</label> If for some reason you don't want the hidden input. Set $attributes['legend'] to false to remove them.g. Use $attributes['separator'] to specify HTML in between radio buttons (e.7.5. <?php echo $this->Form->password('password').3. echo $this->Form->radio('gender'. setting $attributes['value'] to a selected value or boolean false will do just that. Radio elements are wrapped with a label and fieldset by default. array $attributes) Creates a radio button input. ?> Will output: <input name="data[User][password]" value="" id="UserPassword" type="password"> 7.'F'=>'Female'). null. Defaults to true.5. If the supplied $caption is a URL to an image (it contains a ‘. $options. 'F' => 'Female'). ?> Will output: <div class="submit"><input value="Submit" type="submit"></div> . $options) ?> Will output: <select name="data[User][gender]" id="UserGender"> <option value=""></option> <option value="M">Male</option> <option value="F">Female</option> </select> The select input type allows for a special $option attribute called 'escape' which accepts a bool and determines whether to HTML entity encode the contents of the select options. array('escape' => false)). echo $this->Form->select('gender'. <?php echo $this->Form->submit().7. array $options. <?php $options = array('M' => 'Male'. 'F' => 'Female'). It is enclosed between div tags by default. ?> 7. mixed $selected.3. with the option specified by $selected shown as selected by default.3. populated with the items in $options.5. add your string value to the 'empty' key in the $attributes variable.17 select select(string $fieldName. the submit button will be rendered as an image. array $options) Creates a submit button with caption $caption. you can avoid this by declaring $options['div'] = false. If you wish to display your own default option. or set it to false to turn off the default empty option <?php $options = array('M' => 'Male'. echo $this->Form->select('gender'.’ character).18 submit submit(string $caption. array $attributes) Creates a select element. echo $this->Form->input('notes'.png"></div> 7. ?> Will output: <div class="submit"><input type="image" src="/img/ok. ?> Will output: <textarea name="data[User][notes]" id="UserNotes"></textarea> The textarea input type allows for the $options attribute of 'escape' which determines whether or not the contents of the textarea should be escaped. Defaults to true.You can also pass a relative or absolute url to an image for the caption parameter instead of caption text.. <?php echo $this->Form->text('first_name'). <?php echo $this->Form->submit('ok. array('escape' => false).3.png').5.19 text text(string $fieldName.5.3. ?> Will output: <input name="data[User][first_name]" value="" id="UserFirstName" type="text"> 7. array('type' => 'textarea'..20 textarea textarea(string $fieldName. // OR. <?php echo $this->Form->textarea('notes'). <?php echo $this->Form->textarea('notes'. ?> . array $options) Creates a text input field. 'escape' => false). array $options) Creates a textarea input field.. 3 you can declare a set of default options for input() with the inputDefaults key. echo $this->Form->input('password'). Model introspection Support for adding 'required' classes. // has a label element Omit attributes You can now set any attribute key to null or false in an options/attributes array to omit that attribute from a particular html tag. In 1. 'div' => false ) )). In the past only 1 model and a limited set of associations would be introspected. This posed significant limitations on form input creation in some contexts. // No div. and has had several improvements made to it.2 there was a hard limit of 5 nested keys.3. Instead in 1. In 1. Entity depth limitations In 1.7. . echo $this->Form->create('User'. Validation errors and value reading for arbitrary depths has also been added.3 you can now create infinitely nested form element keys. or 'label' => false you would need to set those options on each and every call to input(). array('label' => 'Username')).3 improvements The FormHelper is one of the most frequently used classes in CakePHP. Default options for input() In the past if you needed to use 'div' => false.3 models are introspected as needed. and properties like maxlength to hasMany and other associations has been improved. no label echo $this->Form->input('username'. providing validation and additional information such as maxlength. All inputs created from that point forward would inherit the options declared in inputDefaults. array( 'inputDefaults' => array( 'label' => false.6 1. You can override the defaultOptions by declaring the option in the input() call. Removed parameters Many methods such as select. button() button() now creates button elements. Disabling hidden inputs for radio and checkbox The automatically generated hidden inputs for radio and checkbox inputs can be disabled by setting the 'hiddenField' option to false. Default url The default url for forms either was add or edit depending on whether or not a primary key was detected in the data array. minute. it can be overridden or removed using the 'encoding' option when calling create(). Use the type option to change the default type of button generated. // To remove the accept-charset attribute. meridian and datetime took a $showEmpty parameter. submit() Due to changes in button(). The former features of FormHelper::button have been moved to FormHelper::submit.echo $this->Form->input('username'. array('encoding' => null)). year. // Omits the 'class' attribute added by default to div tag Accept-charset Forms now get an accept-charset set automatically. hour. In addition to creating all types of buttons. You can enable html escaping using the escape option. array( 'div' => array('class' => false) )). In 1. it will match the value of App. submit() has before and after options that behave exactly like their counterparts in input().3 the default url will be the current action.encoding. . echo $this->Form->create('User'. and other types of input buttons. submit() can now generate reset. making the forms submit to the action you are currently on. day. month. these have all been removed and rolled into the $attributes parameter using the 'empty' key. these elements by default do not have html entity encoding enabled. $options['format'] The HTML generated by the form helper is now more flexible than ever before. Si tu deseas activar automáticamente la salida para generar HTML con el ayudante simplemente implementa output() en tu AppHelper. esto te permite hilvanar cualquier atributo extra en tus tags. y más flexibilidad en cuanto a donde se encuentre en relación al dominio principal. Antes de dar un vistaso a los metodos de HtmlHelper. 'input'. 'after'. 7. Los metodos relacionados a Form ya no se usan y han sido movidos al nuevo FormHelper. Revisa lo nuevo de FormHelper. vas a necesitar conocimientos sobre configuración y usos en las situaciones que te puede ayudar esta clase. y más resistentes al cambio. 'value' => 'bar') .1. Usando este ayudante pondremos más luz sobre tu aplicación. en un esfuerzo para aliviar a aquellos que gustan de usar las etiquetas cortas (<?= ?>) o muchas llamadas a echo() en el codigo de sus vistas todos los metodos del HtmlHelper son pasados por el metodo output(). 'error').3 users. Si tu estas buscando ayuda para los formularios HTML. El rol del HtmlHelper ha cambiado significativamente desde CakePHP 1. 'between'. Muchos metodos HtmlHelper incluyen parametros $htmlAttributes. It's just been recently added to SCM. and has a few bugs for non PHP 5. En primer lugar. Aquí hay algunos ejemplos de como se usan los parámetros $htmlAttributes Atributos Deseados: <tag class="algunaClase" /> Arreglo de Parametros: array('class'=>'algunaClase') Atributos Deseados: <tag name="foo" value="bar" /> Arreglo de Parametros: array('name' => 'foo'. rápidas. 'label'.4 HTML El rol del HtmlHelper de CakePHP es hacer los tags referentes a HTML y sus opciones simples. The supported array keys are array('before'. } Haciendo esto no necesitarás agregar llamadas a echo en el codigo de tus vistas. The $options parameter to Form::input() now supports an array of strings describing the template you would like said element to follow. but should be quite useful for all. function output($cadena) { echo $cadena. Don't be afraid to use it often .1. Si $inline está en false.you can cache views in CakePHP in order to save some CPU cycles when views are being rendered and delivered. 7. los tags de enlace son agregados en la variable $scripts_for_layout. 7. This section will cover some of the methods of the HtmlHelper and how to use them.1 Inserting Well-Formatted elements The most important task the HtmlHelper accomplishes is creating well formed markup. es por lo general debido a su nombre que esta faltando y puede configurarlo manualmente en la variable $helpers del controlador. ?> Generará como salida: <meta http-equiv="Content-Type" content="text/html. string $rel = null. charset=ISO-8859-1" /> 7.2 css css(mixed $path. Este método de inclusión de CSS asume que el archivo CSS especificado esté en el directorio . boolean $inline = true) Crea un enlace a una o más hojas de estilos CSS.4. ?> Generará como salida: <meta http-equiv="Content-Type" content="text/html. array $htmlAttributes = array(). charset=utf-8" /> O sino tambien: <?php echo $html->charset('ISO-8859-1').El HtmlHelper está disponible en todas las vistas de forma predeterminada. El valor por defecto es UTF-8.1 charset charset(string $charset=null) Usada para crear metadatos que especificarán la codificación de los caracteres del documento.4.1. la cual puedes imprimir dentro del tag head del documento. Si usted está recibiendo un error que le informa que no está ahí.4. <?php echo $html->charset(). ico'. ?> Hará la salida: <link rel="stylesheet" type="text/css" href="/es/css/forms. string $url = null.'tables'. '/favicon. puede especificar si desea o no que esta etiqueta a aparezca en la línea o en la etiqueta de la cabecera con el cuarto parámetro.css" /> <link rel="stylesheet" type="text/css" href="/es/css/tables. ?> Hará la salida: <link rel="stylesheet" type="text/css" href="/es/css/forms. Si establece el atributo "type" usando el parámetro $htmlAttributes.1.?> //Salida (saltos de linea añadidos) </p> .ico'.3 meta meta(string $type. boolean $inline = true) Este método es útil para vincular a los recursos externos como los feeds RSS / Atom y favicons.4.css" /> El primer parámetro puede ser un arreglo para incluir varios archivos. array $attributes = array().css" /> <link rel="stylesheet" type="text/css" href="/es/css/menu.css" /> 7.'menu')). <?php echo $html->css(array('forms'. array('type' => 'icon') ). CakePHP contiene algunos atajos: type html rss atom icon valor traducido text/html application/rss+xml application/atom+xml image/x-icon <?php echo $html->meta( 'favicon. <?php echo $html->css('forms')./app/webroot/css. Como CSS (). com/comments/index.ico" title="favicon.?> //Salida <meta name="description" content="ingrese alguna descripcion meta aquí"/> Si deseas añadir una etiqueta meta personalizada en el primer parámetro se debe establecer una matriz. '/comments/index.rss'.ico" type="image/x-icon" rel="alternate" /> <?php echo $html->meta( 'Comments'.<link href="http://example. Ejemplo: <?php echo $html->meta( 'keywords'. 'content' => 'noindex')). Para una salida de la etiqueta "robots noindex" debe utilizar el siguiente código: echo $html->meta(array('name' => 'robots'. 'ingrese alguna descripcion meta aquí' ).?> //Salida <meta name="keywords" content="ingrese las palabas claves aquí"/> // <?php echo $html->meta( 'description'. . ?> //Salida (saltos de linea añadidos) <link href="http://example. array('type' => 'rss')).rss" title="Comments" type="application/rss+xml" rel="alternate" /> Este método también puede utilizarse para agregar las etiquetas "meta" para las palabras claves y las descripciones.com/favicon. 'ingrese las palabas claves aquí' ). dtd"> <?php echo $this->Html->docType('html4-trans'). <?php echo $html->style(array( 'background' => '#633'.org/TR/html4/loose. ?> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4. 'border-bottom' => '1px solid #000'.1.4. border-bottom:1px solid #000. 'padding' => '10px' )).7.w3. .5 style style(array $data.4 docType docType(string $type = 'xhtml-strict') Returns a (X)HTML doctype tag. boolean $oneline = true) Construye una definición de estilo CSS basada en las claves y valores del vector pasado al metodo. Supply the doctype according to the following table: type html html4-strict html4-trans html4-frame xhtml-strict xhtml-trans xhtml-frame xhtml11 translated value text/html HTML4 Strict HTML4 Transitional HTML4 Frameset XHTML1 Strict XHTML1 Transitional XHTML1 Frameset XHTML 1.4. ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.dtd"> 7.01 Transitional//EN" "http://www. ?> Mostrara: background:#633.org/TR/xhtml1/DTD/xhtml1-strict.w3.0 Strict//EN" "http://www.1.1 <?php echo $this->Html->docType(). padding:10px. Especialmente util si tu archivo CSS es dinamico. 6) )). la ruta especificada será relativa a /app/webroot/img/.7 link link(string $title. string $confirmMessage = false) General purpose method for creating HTML links. <?php echo $this->Html->link('Enter'. mixed $url = null. . Use $options to specify attributes for the element and whether or not the $title should be escaped.7.1. ?> Will output: <a href="/es/pages/home" class="button" target="_blank">Enter</a> Specify $confirmMessage to display a javascript confirm() dialog. array( "alt" => "Bizcochos". array $options = array(). '/pages/home'. 'url' => array('controller' => 'recipes'. array('class'=>'button'. <?php echo $html->image("recipes/6. 'action' => 'view'. <?php echo $html->image('cake_logo. array $htmlAttributes = array()) Crea una etiqueta de imagen.'target'=>'_blank')). array('alt' => 'CakePHP'))?> Mostrará: <img src="/img/cake_logo. ?> Mostrará: <a href="/es/recipes/view/6"> <img src="/img/recipes/6.1.jpg".4.jpg" alt="Bizcochos" /> </a> 7.4.6 image image(string $path.png" alt="CakePHP" /> Si desea crear un link asociado a la imagen especifique el link de destino usando la opción url option en $htmlAttributes.png'. 'action' => 'view'. 1. <?php echo $this->Html->link('View image'.?> Will output: <a href="/es/recipes/delete/6" onclick="return confirm('Are you sure you wish to delete this recipe?').jpg". array('controller'=>'recipes'.jpg" alt="Brownies" /> </a> Also check HtmlHelper::url method for more examples of different types of urls. 'action'=>'delete'. "Are you sure you wish to delete this recipe?" ). set the escape option to false in the $options array.">Delete</a> Query strings can also be created with link(). To disable this conversion. array( 'controller' => 'images'. <?php echo $this->Html->link( $this->Html->image("recipes/6. . ?> Will output: <a href="/es/recipes/view/6"> <img src="/img/recipes/6. array('escape'=>false) ). 'width' => 500)) ).<?php echo $this->Html->link( 'Delete'. array(). Will output: <a href="/es/images/view/1?height=400&width=500">View image</a> HTML special characters in $title will be converted to HTML entities. 6). '?' => array( 'height' => 400. array("alt" => "Brownies")). "recipes/view/6". $text will be printed HTML-escaped. array $htmlAttributes) Returns text wrapped in a specified tag. <?php echo $this->Html->tag('span'.'). array $options) Used for creating div-wrapped sections of markup.1. If no text is supplied. 'Please enter your credit card number.9 div div(string $class. only an opening div tag is returned.?> //Output <p>Hello World.?> //Output <div class="error">Please enter your credit card number. <?php echo $this->Html->div('error'.4. string $text. 'Hello World.?> //Output <span class="welcome"> 7.</p> .? > //Output <span class="welcome">Hello World</span> //No text specified. string $text.7. 'Hello World. <?php echo $this->Html->tag('span'.10 para para(string $class. only a starting <p> tag is returned. null. boolean $escape = false) Returns a text wrapped in a CSS-classed <p> tag.1.8 tag tag(string $tag. <?php echo $this->Html->para(null.</div> 7.4.'. array('class' => 'welcome')). and the second is used to supply the text to be wrapped by div tags.4. If the last parameter has been set to true. string $text. If no text is specified.1. array('class' => 'welcome')). The first parameter specifies a CSS class. If no text is specified then only the opening <tag> is returned. array $htmlAttributes.'). 4. ?> The first parameter can be an array to include multiple files.11 script script(mixed $url. the attributes will be applied to all of the generated script tags. whether or not you want to include this script once per request or more than once. <?php echo $this->Html->script(array('jquery'. ?> Will output: <script type="text/javascript" href="/es/js/jquery. If an array of script tags is used.js"></script> <script type="text/javascript" href="/es/js/scripts.'wysiwyg'. This method of javascript file inclusion assumes that the javascript file specified resides inside the /app/webroot/js directory. $options['once'] controls. Include a script file into the page. Also new is the ability to add attributes to script .'scripts')). You can also use $options to set additional properties to the generated script tag.12 scriptBlock scriptBlock($code. mixed $options) Creates link(s) to a javascript file.js"></script> <script type="text/javascript" href="/es/js/wysiwyg. If key inline is set to false in $options.js"></script> 7.1. the link tags are added to the $scripts_for_layout variable which you can print inside the head tag of the document. ?> Will output: <script type="text/javascript" href="/es/js/scripts.js"></script> You can link to files with absolute paths as well to link files that are not in app/webroot/js <?php echo $this->Html->script('/otherdir/script_file'). <?php echo $this->Html->script('scripts').1. $options['inline'] controls whether or not a script should be returned inline or added to $scripts_for_layout.4.7. $options = array()) Generate a code block containing $code set $options['inline'] to false to have the script block appear in $scripts_for_layout. 4.?> //Salida <tr> <th>Fecha</th> <th>Nombre</th> <th>Activo</th> </tr> <?php echo $html->tableHeaders( array('Fecha'. $this->Html->scriptEnd().'Nombre'. 7.4.'Nombre'.13 scriptStart scriptStart($options = array()) Begin a buffering code block.1. An example of using scriptStart() and scriptEnd() would be: $this->Html->scriptStart(array('inline' => false)).4. returns the generated script element or null if the script block was opened with inline = false. array $trOptions = null.14 scriptEnd scriptEnd() End a buffering script block.?> .'Activo')). array('class' => 'tabla_productos') ). will create a script tag with defer="defer" attribute. array('defer' => true)).1. <?php echo $html->tableHeaders(array('Fecha'. Options are the same as scriptBlock() 7. echo $this->Js->alert('I am in the javascript'). array $thOptions = null) Crea una fila de encabezados de tabla para ser usados dentro de la etiqueta <table>.1.tags. $this->html->scriptBlock('stuff'. 7.'Activo'). This code block will capture all output between scriptStart() and scriptEnd() and create an script tag. array('class' => 'estado').15 tableHeaders tableHeaders(array $names. array('Best Brownies'. array('Aug 1st. )). )). ?> //Output <tr><td>Jul 7th. $useCount = false.//Salida <tr class="estado"> <th class="tabla_productos">Fecha</th> <th class="tabla_productos">Nombre</th> <th class="tabla_productos">Activo</th> </tr> 7. 2007</td><td>Best Brownies</td><td>Yes</td></tr> <tr><td>Jun 21st. 2007'. 'Yes'). array('Jun 21st. array('Aug 1st.and even-numbered rows. array('No'. 'Yes'). array('Jun 21st. 2006</td><td>Anti-Java Cake</td><td id="special">No</td></tr> . array $evenTrOptions = null. 'Best Brownies'. 2007</td><td>Smart Cookies</td><td>Yes</td></tr> <tr><td>Aug 1st. 2006'. 2007'. 2007'. 2007</td><td class="highlight">Best Brownies</td><td>Yes</td></tr> <tr><td>Jun 21st. ?> //Output <tr><td>Jul 7th. <?php echo $this->Html->tableCells(array( array('Jul 7th. Wrap a single table cell within an array() for specific <td>-attributes. 2006'. array $oddTrOptions = null. 2007'. 'Yes'). 'Yes'). 'Anti-Java Cake'.16 tableCells tableCells(array $data. $continueOddEven = true) Creates table cells. 'Smart Cookies'. 'Smart Cookies'. 2006</td><td>Anti-Java Cake</td><td>No</td></tr> <?php echo $this->Html->tableCells(array( array('Jul 7th. 'Anti-Java Cake'.4. 'No'). array('class'=>'highlight')) . 2007</td><td>Smart Cookies</td><td>Yes</td></tr> <tr><td>Aug 1st. array('id'=>'special'))). in rows.1. assigning <tr> attributes differently for odd. array('class' => 'darker') ). 'Banana'). en caso contrario genera el URL para la combinación de controlador y acción.4. ?> //Output <tr class="darker"><td>Red</td><td>Apple</td></tr> <tr><td>Orange</td><td>Orange</td></tr> <tr class="darker"><td>Yellow</td><td>Banana</td></tr> 7. ). <?php echo $html->url(array( "controller" => "posts". Si $url está vacío devuelve el valor de REQUEST_URI. array('Yellow'. 'Apple'). Si $full es true. ?> // Salida /posts/view/foo:bar . 'Orange').<?php echo $this->Html->tableCells( array( array('Red'.?> // Salida /posts/view/bar Enseguida más ejemplos de uso: URL con parámetros nombrados (named parameters) <?php echo $html->url(array( "controller" => "posts". se antepondrá el URL base del sitio al resultado. "action" => "view". "action" => "view". "bar")). array('Orange'. "foo" => "bar")).17 url url(mixed $url = NULL.1. boolean $full = false) Devuelve un URL que apunta a alguna combinación de controlador y acción. "#" => "primero"))..rss URL (empezando con '/') con el URL completo del sitio agregado al inicio. however if you need to generate HTML for HTML4 you will need to create and load a new tags config file containing the tags you'd like to use. .URL con extensión <?php echo $html->url(array( "controller" => "posts". 'input' => '<input name="%s" %s >'.com/posts URL con parámetros GET y ancla nombrada (named anchor) <?php echo $html->url(array( "controller" => "posts".php containing: $tags = array( 'metalink' => '<link href="%s"%s >'. ?> //Salida http://www.example..4. ?> // Salida /posts/list. <?php echo $html->url('/posts'. //. "?" => array("foo" => "bar"). true). "action" => "list". ?> //Salida /posts/buscar?foo=bar#primero Por mas info ver el Router::url en el API. "ext" => "rss")). ).2 Changing the tags output by HtmlHelper The built in tag sets for HtmlHelper are XHTML compliant. 7. "action" => "buscar". To change the tags used create app/config/tags. You can then load this tag set by calling $html->loadConfig('tags'). 5 Js Since the beginning CakePHP's support for Javascript has been with Prototype/Scriptaculous. To include it in all pages. there . By default scripts are cached. var $helpers = array('Js' => array('Jquery')). the jQuery engine will be used as the default. We created an Adapter based helper. As mentioned before. include this line just before the ending </body> tag echo $this->Js->writeBuffer().ctp if you have not created your own). and you must explicitly print out the cache.5. Prototype/Scriptaculous. Javascript engine selection is declared when you include the helper in your controller. The above would use the Jquery Engine in the instances of JsHelper in your views. Mootools/Mootools-more. And while the API is not as expansive as the previous AjaxHelper we feel that the adapter based solution allows for a more extensible solution giving developers the power and flexibility they need to address their specific application needs. Rather than drop Prototype in favour of another Javascript library. To do this at the end of each page. add this line to the <head> section of app/views/layouts/default. A Javascript engine translates an abstract Javascript element into concrete Javascript code specific to the Javascript library being used.7. and included 3 of the most requested libraries. and jQuery/jQuery UI. // Write cached scripts You must include the library in your page and print the cache for the helper to function.1 Using a specific Javascript engine First of all download your preferred javascript library and place it in app/webroot/js Then you must include the library in your page. If you do not declare a specific engine. While we still think these are an excellent Javascript library. 7.ctp (copy this file from cake/libs/view/layouts/default. echo $this->Html->script('jquery'). In addition they create an extensible system for others to use. the community has been asking for support for other libraries.js will be added to the name). // Include jQuery library Replace jquery with the name of your library file (. Javascript Engines form the backbone of the new JsHelper. allowing you to collect scripts throughout the view.noConflict(). $this>Js->get() returns a $this. Using the facade features of the JsHelper allows you to leverage the buffering and method chaining features built-in.are three engines implemented in the core. but we encourage the community to expand the library compatibility.2 Creating a Javascript Engine Javascript engine helpers follow normal helper conventions. As a general rule. They must have the Engine suffix. Since most methods in Javascript begin with a selection of elements in the DOM. and acts as a facade for the the Engine helper. (method chaining only works in PHP5). You should not directly access the Engine helper except in rare occasions. That said. and output it in one place. Method chaining . this will return the buffer contents in a script tag. # Using jQuery with other libraries The jQuery library. so you shouldn't get a clash between jQuery and any other library (like Prototype. DojoEngineHelper is correct. Furthermore. $this->Js->JqueryEngine->jQueryObject = '$j'. array('inline' => false)). use the jQueryObject variable. or YUI). print $this->Html->scriptBlock('var $j = jQuery. The JsHelper by default buffers almost all script code generated. allowing you to chain the methods using the selection. DojoHelper is not good. they should extend JsBaseEngineHelper in order to leverage the most of the new API.3 Javascript engine usage The JsHelper provides a few methods. and virtually all of its plugins are constrained within the jQuery namespace. Outputting buffered scripts is done with $this->Js->writeBuffer(). //Tell jQuery to go into noconflict mode 7. MooTools. 7. You can disable buffering wholesale with the $bufferScripts property or setting buffer => false in methods taking $options.5. "global" objects are stored inside the jQuery namespace as well.5. elements and layout. with a few additional restrictions. jQuery uses "$" as a shortcut for "jQuery" To override the "$" shortcut. there is one caveat: By default.'. each library also supports all of its native callbacks and options. The new JsHelper if used correctly avoids both of those issues. Method chaining is not possible in PHP4 and the above sample would be written like: $this->Js->get('#foo'). $eventCode). # Common options In attempts to simplify development where Js libraries can change. If you are not planning on switching Javascript libraries.allows you to write shorter. these common options will be mapped out to the library specific options internally. Is an example of method chaining. 7. . # Callback wrapping By default all callback options are wrapped with the an anonymous function with the correct arguments. $this->Js->get('#foo')->event('click'.1 Working with buffered scripts One drawback to previous implementation of 'Ajax' type features was the scattering of script tags throughout your document.5. It is recommended that you place $this>Js->writeBuffer() at the bottom of your layout file above the </body> tag.3. This will allow all scripts generated in layout elements to be output in one place. a common set of options is supported by JsHelper. and the inability to buffer scripts added by elements in the layout. It should be noted that buffered scripts are handled separately from included script files. writeBuffer($options = array()) Writes all Javascript generated so far to a code block or caches them to a file and returns a linked script. You can disable this behavior by supplying the wrapCallbacks = false in your options array. $eventCode). more expressive code. $this->Js->event('click'. It should be noted that method chaining Will not work in PHP4. Options • inline . Buffering methods that are not normally buffered Some methods in the helpers are buffered by default.. ]]> (default true) Creating a cache file with writeBuffer() requires that webroot/js be world writable and allows a browser to cache generated script resources for any page. a script link tag will be generated. getBuffer($clear = true) Get the contents of the current buffer. The engines buffer the following methods by default: • • • • • event sortable drag drop slider Additionally you can force any other method in JsHelper to use the buffering. true).Set to true to have scripts output as a script block inline if cache is also true. . buffer($content) Add $content to the internal script buffer. $this->Js->each('alert("whoa!").Set to false to prevent script cache from being cleared (default true) • onDomReady .wrap cached scripts in domready event (default true) • safe . $this->Js->event('click'.Set to true to have scripts cached to a file and linked in (default false) • clear . (default true) • cache . you can pass a false in as the last argument. false). This would force the event function which normally buffers to return its result. 'alert("whoa!").'.if an inline block is generated should it be wrapped in <![CDATA[ . The above would force the each() method to use the buffer. For example the each() method does not normally buffer.'.. Pass in false to not clear the buffer at the same time. By appending an boolean to the end of the arguments you can force other methods to go into the buffer. Conversely if you want a method that does buffer to not buffer. The following list of methods are supported by all the Engines included in the CakePHP core.String prepended to the returned data. There are a few differences between this method and JavascriptHelper::object(). there is also a subset of common options that are translated into library specific options. $options = array()) Converts values into JSON. object($data. • postfix . Only this element will start sort action.5. Most notably there is no affordance for stringKeys or q options found in the JavascriptHelper. The normalized options are: Options • containment . sortable($options = array()) Sortable generates a javascript snippet to make a set of elements (usually a list) drag and drop sortable.Selector to handle element. Example Use: $json = $this->Js->object($data). Whenever you see separate lists for Options and Event Options both sets of parameters are supplied in the $options array for the method. This is done to provide end developers with as unified an API as possible. cannot make script tags.Container for move action • handle .7. Furthermore $this->Js->object(). Options: • prefix .Whether or not to use an effect to move sortable into final position. Event Options • start . • opacity .4 Methods The core Javascript Engines provide the same feature set across all libraries.Event fired when sorting starts .Distance a sortable must be dragged before sorting starts. • revert .String appended to the returned data.Opacity of the placeholder • distance . Callback to fire on request failure. • success . 'complete' => 'onStop'.Callback to fire on complete. distance:5. . sort:onSort. Other options are supported by each Javascript library. and you should check the documentation for your javascript library for more detailed information on its options and parameters. • error . $options = array()) Generate a javascript snippet to create an XmlHttpRequest or 'AJAX' request. • data . request($url. Options • method . start:onStart. 'sort' => 'onSort'.Additional data to send.Event fired during sorting • complete .sortable({containment:"parent". 'containment' => 'parent'.Event fired when sorting completes. • before .• sort . stop:onStop}). 'start' => 'onStart'.Callback to fire on request initialization.Callback to fire on success. Event Options • complete . Example use: $this->Js->get('#my-list'). you would get the following code in your generated Javascript block: $("#myList").Whether or not you want an asynchronous request. 'wrapCallbacks' => false )). $this->Js->sortable(array( 'distance' => 5.The method to make the request with defaults to GET in more libraries • async . Assuming you were using the jQuery engine. • update - Dom id to update with the content of the request. • type - Data type for response. 'json' and 'html' are supported. Default is html for most libraries. • evalScripts - Whether or not <script> tags should be eval'ed. • dataExpression - Should the data key be treated as a callback. Useful for supplying $options['data'] as another Javascript expression. Example use $this->Js->event('click', $this->Js->request(array( 'action' => 'foo', param1), array( 'async' => true, 'update' => '#element'))); get($selector) Set the internal 'selection' to a CSS selector. The active selection is used in subsequent operations until a new selection is made. $this->Js->get('#element'); The JsHelper now will reference all other element based methods on the selection of #element. To change the active selection, call get() again with a new element. drag($options = array()) Make an element draggable. Options • handle - selector to the handle element. • snapGrid - The pixel grid that movement snaps to, an array(x, y) • container - The element that acts as a bounding box for the draggable element. Event Options • start - Event fired when the drag starts • drag - Event fired on every step of the drag • stop - Event fired when dragging stops (mouse release) Example use $this->Js->get('#element'); $this->Js->drag(array( 'container' => '#content', 'start' => 'onStart', 'drag' => 'onDrag', 'stop' => 'onStop', 'snapGrid' => array(10, 10), 'wrapCallbacks' => false )); If you were using the jQuery engine the following code would be added to the buffer. $("#element").draggable({containment:"#content", start:onStart, stop:onStop}); drag:onDrag, grid:[10,10], drop($options = array()) Make an element accept draggable elements and act as a dropzone for dragged elements. Options • accept - Selector for elements this droppable will accept. • hoverclass - Class to add to droppable when a draggable is over. Event Options • drop - Event fired when an element is dropped into the drop zone. • hover - Event fired when a drag enters a drop zone. • leave - Event fired when a drag is removed from a drop zone without being dropped. Example use $this->Js->get('#element'); $this->Js->drop(array( 'accept' => '.items', 'hover' => 'onHover', 'leave' => 'onExit', 'drop' => 'onDrop', 'wrapCallbacks' => false )); If you were using the jQuery engine the following code would be added to the buffer: <code class= "php">$("#element").droppable({accept:".items", over:onHover});</code> drop:onDrop, out:onExit, ''Note'' about MootoolsEngine::drop Droppables in Mootools function differently from other libraries. Droppables are implemented as an extension of Drag. So in addtion to making a get() selection for the droppable element. You must also provide a selector rule to the draggable element. Furthermore, Mootools droppables inherit all options from Drag. slider() Create snippet of Javascript that converts an element into a slider ui widget. See your libraries implementation for additional usage and features. Options • handle - The id of the element used in sliding. • direction - The direction of the slider either 'vertical' or 'horizontal' • min - The min value for the slider. • max - The max value for the slider. • step - The number of steps or ticks the slider will have. • value - The initial offset of the slider. Events • change - Fired when the slider's value is updated • complete - Fired when the user stops sliding the handle Example use $this->Js->get('#element'); $this->Js->slider(array( 'complete' => 'onComplete', 'change' => 'onChange', 'min' => 0, 'max' => 10, 'value' => 2, 'direction' => 'vertical', 'wrapCallbacks' => false )); If you were using the jQuery engine the following code would be added to the buffer: $("#element").slider({change:onChange, stop:onComplete, value:2}); max:10, min:0, orientation:"vertical", effect($name, $options = array()) Creates a basic effect. By default this method is not buffered and returns its result. Supported effect names The following effects are supported by all JsEngines • show - reveal an element. • hide - hide an element. • fadeIn - Fade in an element. • fadeOut - Fade out an element. • slideIn - Slide an element in. • slideOut - Slide an element out. Options • speed - Speed at which the animation should occur. Accepted values are 'slow', 'fast'. Not all effects use the speed option. Example use If you were using the jQuery engine. $this->Js->get('#element'); $result = $this->Js->effect('fadeIn'); //$result contains $("#foo").fadeIn(); event($type, $content, $options = array()) Bind an event to the current selection. $type can be any of the normal DOM events or a custom event type if your library supports them. $content should contain the function body for the callback. Callbacks will be wrapped with function (event) { ... } unless disabled with the $options. Options • wrap - Whether you want the callback wrapped in an anonymous function. (defaults to true) • stop - Whether you want the event to stopped. (defaults to true) Example use $this->Js->get('#some-link'); $this->Js->event('click', $this->Js->alert('hey you!')); If you were using the jQuery library you would get the following Javascript code. $('#some-link').bind('click', function (event) { alert('hey you!'); return false; }); You can remove the return false; by passing setting the stop option to false. $this->Js->get('#some-link'); $this->Js->event('click', $this->Js->alert('hey you!'), array('stop' => false)); If you were using the jQuery library you would the following Javascript code would be added to the buffer. Note that the default browser event is not cancelled. $('#some-link').bind('click', function (event) { alert('hey you!'); }); domReady($callback) Creates the special 'DOM ready' event. writeBuffer() automatically wraps the buffered scripts in a domReady method. each($callback) Create a snippet that iterates over the currently selected elements, and inserts $callback. Example $this->Js->get('div.message'); $this->Js->each('$(this).css({color: "red"});'); Using the jQuery engine would create the following Javascript $('div.message').each(function () { $(this).css({color: "red"});}); alert($message) Create a javascript snippet containing an alert() snippet. By default, alert does not buffer, and returns the script snippet. $alert = $this->Js->alert('Hey there'); confirm($message) Create a javascript snippet containing a confirm() snippet. By default, confirm does not buffer, and returns the script snippet. $alert = $this->Js->confirm('Are you sure?'); prompt($message, $default) Create a javascript snippet containing a prompt() snippet. By default, prompt does not buffer, and returns the script snippet. $prompt = $this->Js->prompt('What is your favorite color?', 'blue'); submit() Create a submit input button that enables XmlHttpRequest submitted forms. Options can include both those for FormHelper::submit() and JsBaseEngine::request(), JsBaseEngine::event(); Forms submitting with this method, cannot send files. Files do not transfer over XmlHttpRequest and require an iframe, or other more specialized setups that are beyond the scope of this helper. Options • confirm - Confirm message displayed before sending the request. Using confirm, does not replace any before callback methods in the generated XmlHttpRequest. • buffer - Disable the buffering and return a script tag in addition to the link. • wrapCallbacks - Set to false to disable automatic callback wrapping. Example use echo $this->Js->submit('Save', array('update' => '#content')); Will create a submit button with an attached onclick event. The click event will be buffered by default. echo $this->Js->submit('Save', array('update' 'type' => 'json', 'async' => false)); => '#content', 'div' => false, Shows how you can combine options that both FormHelper::submit() and Js::request() when using submit. link($title, $url = null, $options = array()) Create an html anchor element that has a click event bound to it. Options can include both those for HtmlHelper::link() and JsBaseEngine::request(), JsBaseEngine::event(); $htmlAttributes is used to specify additional options that are supposed to be appended to the generated anchor element. If an option is not part of the standard attributes or $htmlAttributes it will be passed to request() as an option. If an id is not supplied, a randomly generated one will be created for each link generated. Options • confirm - Generate a confirm() dialog before sending the event. • id - use a custom id. • htmlAttributes - additional non-standard htmlAttributes. Standard attributes are class, id, rel, title, escape, onblur and onfocus. • buffer - Disable the buffering and return a script tag in addition to the link. Example use echo $this->Js->link('Page 2', array('page' => 2), array('update' => '#content')); Will create a link pointing to /page:2 and updating #content with the response. You can use the htmlAttributes option to add in additional custom attributes. echo $this->Js->link('Page 2', array('page' => 2), array( 'update' => '#content', 'htmlAttributes' => array('other' => 'value') )); //Creates the following html <a href="/es/posts/index/page:2" other="value">Page 2</a> serializeForm($options = array()) Serialize the form attached to $selector. Pass true for $isForm if the current selection is a form element. Converts the form or the form element attached to the current selection into a string/json object (depending on the library implementation) for use with XHR operations. Options • isForm - is the current selection a form, or an input? (defaults to false) • inline - is the rendered statement going to be used inside another JS statement? (defaults to false) Setting inline == false allows you to remove the trailing ;. This is useful when you need to serialize a form element as part of another Javascript operation, or use the serialize method in an Object literal. redirect($url) Redirect the page to $url using window.location. value($value) Converts a PHP-native variable of any type to a JSON-equivalent representation. Escapes any string values into JSON compatible strings. UTF-8 characters will be escaped. 7.5.5 Ajax Pagination Much like Ajax Pagination in 1.2, you can use the JsHelper to handle the creation of Ajax pagination links instead of plain HTML links. # Making Ajax Links Before you can create ajax links you must include the Javascript library that matches the adapter you are using with JsHelper. By default the JsHelper uses jQuery. So in your layout include jQuery (or whichever library you are using) echo $this->Html->script('jquery'); Similar to 1.2 you need to tell the PaginatorHelper that you want to make Javascript enhanced links instead of plain HTML ones. To do so you use options() $this->Paginator->options(array( 'update' => '#content', 'evalScripts' => true )); The PaginatorHelper now knows to make javascript enhanced links, and that those links should update the #content element. Of course this element must exist, and often times you want to wrap $content_for_layout with a div matching the id used for the update option. You also should set evalScripts to true if you are using the Mootools or Prototype adapters, without evalScripts these libraries will not be able to chain requests together. The indicator option is not supported by JsHelper and will be ignored. You then create all the links as needed for your pagination features. Since the JsHelper automatically buffers all generated script content to reduce the number of <script> tags in your source code you must call write the buffer out. At the bottom of your view file. Be sure to include: echo $this->Js->writeBuffer(); If you omit this you will not be able to chain ajax pagination links. When you write the buffer, it is also cleared, so you don't have worry about the same Javascript being output twice. # Adding effects and transitions Since indicator is no longer supported, you must add any indicator effects yourself. <html> <head> <?php echo $this->Html->script('jquery'); ?> //more stuff here. </head> <body> <div id="content"> <?php echo $content_for_layout; ?> </div> <?php indicator')); ?> </body> </html> echo $this->Html->image('indicator.gif', array('id' => 'busy- Remember to place the indicator.gif file inside app/webroot/img folder. You may see a situation where the indicator.gif displays immediately upon the page load. You need to put in this css #busyindicator { display:none; } in your main css file. With the above layout, we've included an indicator image file, that will display a busy indicator animation that we will show and hide with the JsHelper. To do that we need to update our options() function. $this->Paginator->options(array( 'update' => '#content', 'evalScripts' => true, 'before' => $this->Js->get('#busy-indicator')->effect('fadeIn', array('buffer' => false)), 'complete' )); => $this->Js->get('#busy-indicator')->effect('fadeOut', array('buffer' => false)), This will show/hide the busy-indicator element before and after the #content div is updated. Although indicator has been removed, the new features offered by JsHelper allow for more control and more complex effects to be created. 7.6 Javascript EL ayudante Javascript es usado para en la creación de etiquetas y bloques de código javascript bien formados. Existen varios métodos algunos de los cuales están diseñados para trabajar con el framework Javascript Prototype. 7.6.1 Methods codeBlock($script, $safe) • string $script - The JavaScript to be wrapped in SCRIPT tags • array $options - Set of options: • allowCache: boolean, designates whether this block is cacheable using the current cache settings. • safe: boolean, whether this block should be wrapped in CDATA tags. Defaults to helper's $options = array('allowCache'=>true,'safe'=>true,'inline'=>true), object configuration. • inline: whether the block should be printed inline, or written to cached for later output (i.e. $scripts_for_layout). • boolean $safe - DEPRECATED. Use $options['safe'] instead codeBlock returns a formatted script element containing $script. But can also return null if Javascript helper is set to cache events. See JavascriptHelper::cacheEvents(). And can write in $scripts_for_layout if you set $options['inline'] to false. blockEnd() Ends a block of cached Javascript. Can return either a end script tag, or empties the buffer, adding the contents to the cachedEvents array. Its return value depends on the cache settings. See JavascriptHelper::cacheEvents() link($url, $inline = true) • mixed $url - String URL to JavaScript file, or an array of URLs. • boolean $inline If true, the <script> tag will be printed inline, otherwise it will be printed in $scripts_for_layout Creates a javascript link to a single or many javascript files. Can output inline or in $scripts_for_layout. If the filename is prefixed with "/", the path will be relative to the base path of your application. Otherwise, the path will be relative to your JavaScript path, usually webroot/js. escapeString($string) • string $script - String that needs to get escaped. Escape a string to be JavaScript friendly. Allowing it to safely be used in javascript blocks. The characters that are escaped are: • "\r\n" => '\n' • "\r" => '\n' • "\n" => '\n' • '"' => '\"' • "'" => "\\'" If set to true the cached javascript is cleared. Object does not have to be an ID reference it can refer to any valid javascript object or CSS selectors.Whether to fire the event in the capture or bubble phase of event handling.type of event to observe ie 'click'. returns JavaScript event code. $all) • boolean $file .If true. $event. This method requires the Prototype library. Defaults false • boolean $options['allowCache'] . 'over'. If a CSS selector is used the event handler is cached and should be retrieved with JavascriptHelper::getCache().Set options: useCapture.e. Otherwise it is added to the output of $scripts_for_layout in the layout.' i. • string $event . Gets (and clears) the current JavaScript event cache writeEvents($inline) • boolean $inline . If $file was set to true with cacheEvents(). cacheEvents($file. code will be written to a file • boolean $all .Indicates whether <script /> blocks should be written 'safely. . Else it is added to the $scripts_for_layout for the page.event($object. If $all is set to true. $observer. the event code is returned inline. all code written with JavascriptHelper will be sent to a file Allows you to control how the JavaScript Helper caches event code generated by event(). Returns cached javascript code. • string $observer .DOM Object to be observed. all code generated by the helper is cached and can be retrieved with getCache() or written to file or page output with writeCache(). wrapped in CDATA blocks Attach a javascript event handler specified by $event to an element DOM element specified by $object. getCache($clear) • boolean $clear .See JavascriptHelper::cacheEvents() • boolean $options['safe'] . code is cached to a file and a script link to the cached events file is returned. $useCapture) • string $object . If inline is true. • array $options .Javascript function to call when event occurs. allowCache.If true.If true. safe • boolean $options['useCapture'] . Defaults to true. data sizes.The type of quote to use. Includes the contents of each file inline.GBP. $options = array()) This method is used to display a number in common currency formats (EUR. q • boolean $options['block'] . 7. percentages. These methods include ways to format currency. Includes the named $script.Data to be converted • array $options . quoteKeys. stringKeys. Defaults to true.If false.7 Number The NumberHelper contains convenience methods that enable display numbers in common formats in your views. Generates a JavaScript object in JavaScript Object Notation (JSON) from $data array. treats $stringKey as a list of keys *not* to be quoted. prefix. format numbers to specific precisions and also to give you more flexibility with formating numbers. • boolean $options['quoteKeys'] . $options) • array $data .Appends the string to the returned data. • string $options['q'] . 7.A list of array keys to be treated as a string. All of these functions return the formated number. • string $options['postfix'] . object($data. If $script is left blank the helper will include every script in your app/webroot/js directory. • string $options['prefix'] .7. string $currency= 'USD'.File name of script to include. • array $options['stringKeys'] . They do not automatically echo the output into the view.includeScript($script) • string $script .Wraps return value in a <script /> block if true.Prepends the string to the returned data. Usage in a view looks like: . To create a script tag with an src attribute use link(). postfix. Defaults to false.1 currency currency(mixed $number.USD).Set of options: block. Set to boolean false to use no decimal symbol.7. ?> //Outputs: FOO 1. should be a floating point number that represents the amount of money you are expressing. 0. '. ie.$currency).33 The third parameter is an array of options for further defining the output.33 USD $ 1. formatted by currency type EUR € 1. For example: <?php echo $this->Number->currency('1234. '.56. 2 thousands Thousands separator ie.236. It will round in order to maintain the level of precision defined.' decimals Decimal separator symbol ie.234. the number will be wrapped with ( and ) escape Should the output be htmlentity escaped? Defaults to true If a non-recognized $currency value is supplied. The second parameter is used to choose a predefined currency formatting scheme: $currency 1234. ?> //Outputs: 456. eg.236. can be a string or a number. 'c'. 0. $number. 'FOO'). If equal to '()'. '$' The currency symbol to place after decimal numbers ie. int $precision = 3) This method displays a number with the specified amount of precision (decimal places).91873645.56'.2 precision precision (mixed $number.35 => $0. 'Free!' places Number of decimal places to use.35. The following options are available: Option before after Description The currency symbol to place before whole numbers ie.' negative Symbol for negative numbers. ?> The first parameter.236. <?php echo $this->Number->precision(456.92 .33 GBP £ 1.<?php echo $this->Number->currency($number. 2 ). it is prepended to a USD formatted number.56 7. zero The text to use for zero values. ie. 5 format format (mixed $number.7. This method also expresses the number as a percentage and prepends the output with a percent sign. GB. mixed $options=false) This method gives you much more control over the formatting of numbers for use in your views (and is used as the main method by most of the other NumberHelper methods).7. ?> //Outputs: 45. and TB. The $number parameter is the number that you are planning on formatting for output. // 1 KB echo $this->Number->toReadableSize(1321205. // 0 Bytes echo $this->Number->toReadableSize(1024). int $precision = 2) Like precision(). With no $options supplied. Note that the default precision is zero decimal places. this method formats a number according to the supplied precision (where numbers are rounded to meet the given precision). $options). . according to the size of data supplied (i.7.69% 7. the number 1236. // 1.e.691873645). Using this method might looks like: $this->Number->format($number. higher sizes are expressed in larger terms): echo $this->Number->toReadableSize(0).00 GB 7.7. It provides a shortcut way to convert bytes to KB. MB. The size is displayed with a two-digit precision level.4 toReadableSize toReadableSize(string $data_size) This method formats data sizes in human readable forms.334 would output as 1. The $options parameter is where the real magic for this method resides. <?php echo $this->Number->toPercentage(45.76). // 5.26 MB echo $this->Number->toReadableSize(5368709120).236.3 toPercentage toPercentage(mixed $number. '.' )).• If you pass an integer then this becomes the amount of precision or places for the function. Los formatos soportados son 'range' y 'pages' y custon (personalizado) que es el por defecto. Si se suministra un string. 'thousands' => '. options() configura todas las opciones para el Paginator Helper. Las opciones soportadas son: format Formato del contador. Ve también Tareas comunes con CakePHP .79' 7.1 Métodos options($options = array()) • options() : Opciones por defecto de la paginación para los links. En el modo por defecto el string proporcionado es parseado y los tokens son reemplazados por sus verdaderos valores. … places echo $this->Number->format('123456. 'before' => '¥ '. // output '¥ 123.8. 'escape' => false. éste se usa como el id del elemento DOM a updatear.456.Paginacion para más información.7890'. 'decimals' => '. Vea #options para la lista de las llaves posibles. Los tokens disponibles son: . millions. 7.8 Paginator El Paginator helper. array( 'places' => 2. • If you pass an associated array. you can use the following keys: • places (integer): the amount of desired precision • before (string): to be put before the outputted number • escape (boolean): if you want the value in before to be escaped • decimals (string): used to delimit the decimal places in a number • thousands (string): used to mark off thousand. se usa para imprimir los controles de los números de página y de los links siguiente y previo. Valor por defecto 'ASC' • page . Si no esta especificada. Valor por defecto true. puedes usar el método counter() para mostrar todos tipo de información retornada en los resultados.número total de registros en el conjunto resultado. por ejemplo: echo $paginator->counter(array( 'format' => 'Pagina %page% de %pages%. • %current% . El valor por defecto es ' of '. • %pages% . se crearán links regulares. • %start% . Esto se usa en conjunto con formato = 'pages' url La url de la accion de paginación. url tiene algunas sub opciones también • sort .• %page% . . escape Define si el campo de título de los links debería ser sin HTML. update El id del elemento DOM a updatear con los resultados de una llamada AJAX.la dirección en la cual se ordena.número total de páginas.el número de página a mostrar model El nombre del modelo que esta siendo paginado. Ahora que sabes los tokens disponibles. mostrando %current% registros de un total de %count%. separator El separador entre la pagina actual y el numero de paginas. • %count% . • %end% .la llave por la cual los registros estan ordenados • direction . comenzando en el registro %start%.número actual de registros mostrados.numero del ultimo registro mostrado..numero del primer registro mostrado. terminando en el %end%' )).la página actual. Primero debemos activar parseExtensions. Ahora.rss. Despues de unos simples pasos puedes añadir la extension . Cuando usamos Router::parseExtensions() podemos pasar tantos argumentos o extensiones como queramos. debemos hacer algunas pequeñas cosas. 7.1 Creando un RSS feed con el RssHelper Este ejemplo asume que tu tienes creados un controlador de Posts y un modelo de Post y que quieres hacer una vista alternativa para RSS. $url = array(). Crea un link regular o AJAX con los parametros de paginación. Esto activará cada 'extension/content-type' para el uso de nuestra aplicación. ver options() para la lista de llaves. En la llamanda anterior hemos activado la extension .9 RSS El helper RSS hace que generar XML para un RSS feed sea muy fácil.rss a post/index haciendo tu URL posts/index. En segundo lugar una buena idea es agregar RequestHandler al arreglo de componentes $components de PostsController. Esto permitirá que ocurra mucha automagia.rss. echo $paginator->link('Ordenados por título en pagina 5'. Crear una version xml/rss de posts/index es algo muy fácil con CakePHP 1. Ver Router::url() • array $options Opciones para el link. esto se hace en app/config/routes. $options = array()) • string $title . array('sort' => 'title'. Antes de adelantarnos tratando de conseguir que nuestro servicio web quede listo.9. 'direction' => 'desc')). Si se creó en la vista en /posts/index Debería crear un link apuntando a '/posts/index/page:5/sort:title/direction:desc' 7.rss sea requerida obtendrás una versión xml .El título del link.2.indicator El id del elemento DOM que será mostrado como indicador de descarga o trabajo en curso mientras se ejecuta la llamada AJAX. cuando la dirección posts/index. 'page' => 5.php Router::parseExtensions('rss'). link($title. • mixed $url Url para la acción. 7.1.created DESC'. escribe lo siguiente en app/view/layouts/rss/default. $posts = $this->paginate().1. por ahora si tienes alguna lógica diferente para los datos usados en el RSS feed y los datos que se usan en la vista html puedes usar el método RequestHandler::isRss().de posts/index. $this->set(compact('posts')). de otra forma tu controlador puede quedar igual. que en nuestro caso // es la accion index public function index(){ if( $this->RequestHandler->isRss() ){ $posts = $this->Post->find('all'. } } Con todas las variables de vista configuradas necesitamos crear un layout rss.9. array('limit' => 20. 'limit' => 10). 'order' => 'Post. Esta información también ir a la vista.1 Layout RSS Un layout RSS es muy simple.ctp: . Pero eso vendrá después. lo primero que necesitamos es hacer que los archivos de vista que crearán nuestro rss/xml feed. 7. Es tentador poner el canal de metadatos en la accion del controlador y pasarlo a nuestas vistas usando la función Controller::set() pero es es inapropiado. $this->set(compact('posts')).9.1. Sin embargo. entonces entregamos // usamos los datos para la salida html $this->paginate['Post'] = array('order' = 'Post. } else { // esta no es una llamada Rss. // Modificar la accion del controlador de Posts que corresponde // a la cual entrega el rss feed.1 Código para el Controlador Antes de crear nuestra version RSS de posts/index necesitamos poner algunas cosas en orden.created DESC')). 1/')). $channelData. necesitamos crear el directorio views/posts/rss/ y crear un nuevo index. Lo siguiente es el archivo de vista de posts/index. } if (!isset($channelData['title'])) { $channelData['title'] = $title_for_layout.2 tus vistas pueden pasar variables de vuelta al layout. 'link' => $html->url('/'.1. array( 'xmlns:dc' => 'http://purl. en CakePHP 1.org/dc/elements/1. $content_for_layout). } $channel = $rss->channel(array(). Esto se hace utilizando el método View::set() el cual es análogo al método Controller::set(). if (!isset($documentData)) { $documentData = array(). echo $rss->document($documentData. . 'language' => 'en-us')).$channel). estos contienen todos los metadatos para nuestro RSS feed. array( 'title' => __("Artículos más leídos". En este momento nuestro arreglo $channelData entrará en acción para configurar todos los metadatos para nuestro feed. 7. No parece ser la gran cosa. sin embargo gracias al poder del RssHelper hara un monton de cosas por nosotros. true).2 La vista Nuestra vista comienza por configurar las variables $documentData y $channelData para el layout. El contenido de ese archivo está mas abajo.9. $this->set('channelData'. true).". Así como necesitamos un layout. 'description' => __("Artículos más recientes. $this->set('documentData'.1. No hemos configrado $documentData o $channelData en nuestro controlador. true). } if (!isset($channelData)) { $channelData = array().echo $rss->header().ctp dentro. sin embargo. Acá estamos pasando los metadatos del canal de vuelta al layout. Hay un punto débil en este método. true.La segunda parte de la vista genera los elementos para los registros del feed.'. true). $bodyText = $text->truncate($bodyText. 'day' => date('d'. // Acá es donde se limpia el cuerpo del texto para la salida como la descripción // de los items rss. $entry['Entry']['slug']). Esto se consigue haciendo un ciclo a los datos entregados a la vista ($items) y usando el método RssHelper::item(). (El metodo usado para las llamadas siempre a se ha llamado transformRss()). $bodyText = Sanitize::stripAll($bodyText). $postTime). esto necesita tener solo texto para asegurarnos de que valide el feed $bodyText = preg_replace('=\(. foreach ($entries as $entry) { $postTime = strtotime($entry['Entry']['created']). 'pubDate' => $entry['Entry']['created'])). que es que no puedes usar ningún método de otro helper para preparar los datos dentro del metodo de la llamada. El metodo RssHelper::item() transforma el arreglo asociativo en un elemento para cada par llave-valor. El otro método que puedes usar es. // deberías importar Sanitize App::import('Sanitize'). array( 'title' => $entry['Entry']['title']. } . 'description' => $bodyText.*?)\=is'. $entry['Entry']['body']). echo $rss->item(array(). 'action' => 'view'. 400. 'guid' => array('url' => $entryLink. porque el ámbito dentro de la llamada no incluye nada que no se haya entregado desde afuera. $entryLink = array( 'controller' => 'entries'.. 'link' => $entryLink. 'isPermaLink' => 'true'). $bodyText = $text->stripLinks($bodyText).. $postTime). 'year' => date('Y'. '. $postTime). 'month' => date('m'. ''. lo que no da acceso al TimeHelper o cualquier otro que necesitemos. 'dc:creator' => $entry['Entry']['author']. RssHelper::items() el cual toma una llamada y un arreglo de items para el feed. com') ).Puedes ver que podemos usar el loop para preparar los datos para ser transformados en elementos XML.w3. Es importante filtrar cuaquier caracter que no sea de texto plano. pero recomendamos escribir un helper especializado para dejar el texto realmente limpio. no es necesario agregarlo en el arreglo $helpers en el controlador. el nodo sería accesado por User. Al igual que en el Componente de Sesión. el Helper Session refleja la mayoría de las funcionalidades de los componentes y las hace disponible a las vistas. La mayor diferencia entre el Helper y el Componente de Sesión es que el Helper no puede escribir en la sesión. podemos usar el método RssHelper::item() para crear el XML del formato RSS. puedes probar tu RSS dirigiéndote a la direccion /entries/index. con el punto indicando el arreglo enlazado. los datos son escritos y leídos usando estructuras de arreglos separadas por puntos. Esta notación es usada por todos los métodos del Helper de Sesión siempre que se utilice $key.username. especialmente si estas usando un editor de html para el cuerpo de tu blog. Una vez que hayas hecho todo esto. . Dada la estructura de arreglo previa. Siempre es importante que valides tu RSS feed antes de ponerlo en produccion.10 Sesión Como contraparte natural al Componente Session. Una vez que hemos configurado los datos para el feed. El Helper de Sesión se agrega automáticamente a tu vista.rss y verás tu nuevo feed. En el codigo anterior usamos el método TextHelper::stripLinks() y algunos pocos métodos de la clase Sanitize. 7. array('User' => array('username' => '
[email protected]/feed/. Esto se puede hacer visitando algunos sitios como FeedValidator o el sitio de w3c en http://validator. For example.10. Returns a boolean on the key's existence. echo $this->Session->flash('auth'). # Using Flash for Success and Failure In some web sites.Message. It is used in conjunction with the ) error() Session Component's setFlash() method.10. particularly administration backoffice web applications it is often expected that the result of an operation requested by the user has associated feedback as to whether the operation succeeded or not. One way to achieve this is to use Session->flash() with the layout parameter. 7. y) flash($key This will return the contents of the $_SESSION. ) id() Returns the current session ID. You can also retrieve specific keys in the session. This is a classic usage for the flash mechanism since we only want to show the user the result once and not keep the message. Returns a string or array depending on the contents of the session. the Auth component sets all of its Session messages under the 'auth' key // Controller code $this->Session->setFlash('My Message'). In the controller you might typically have code: . // outputs "<div id='flashMessage' class='message'>My Message</div>" // output the AuthComponent Session message. // In view echo $this->Session->flash().2 flash The flash method uses the default key set by setFlash(). With the layout parameter we can be in control of the resultant html for the message.1 Methods read($key Read from the Session. check($ke Check to see if a key is in the Session.7. if set. Returns the last error in the session if one exists. ?> And of course you can then add to your CSS a selector for div. The flash_success and flash_failure parameter represents an element file to place in the root app/views/elements folder. according to any options defined in $htmlOptions (see HtmlHelper::link()). and to gracefully truncating long stretches of text. It aids in enabling links. app/views/elements/flash_success.11 Text The TextHelper contains methods to make text more usable and friendly in your views.flash_success and div.'. .'.flash_failure 7. $linked_text = $this->Text->autoLinkEmails($my_text). div. } else { $this->Session->setFlash('The user could not be deleted.flash. formatting urls. app/views/elements/flash_failure. contact
[email protected]. # autoLinkEmails autoLinkEmails(string $text. array $htmlOptions=array()) Adds links to the well-formed email addresses in $text.ctp. $my_text = 'For more information regarding our world-famous pastries and desserts.com'. highlighting key words in blocks of text.ctp Inside the flash_success element file would be something like this: <div class="flash flash_success"> <?php echo $message ?> </div> The final step is in your main view file where the result is to be displayed to add simply <?php echo $this->Session->flash(). e. } user was deleted successfully. creating excerpts of text around chosen words or phrases.if ($user_was_deleted) { $this->Session->setFlash('The 'flash_success'). 'flash_failure'). string $ending=". The query. $highlighter='<span class="highlight">\1</span>') Highlights $needle in $haystack using the $highlighter string specified. or nntp and links them appropriately.com">info@example. array $htmlOptions=array()) Same as in autoLinkEmails(). array $htmlOptions=array()) Performs the functionality in both autoLinkUrls() and autoLinkEmails() on the supplied $text. 'using').Output: For more information regarding our world-famous pastries and desserts.. only this method searches for strings that start with https. contact <a href="mailto:info@example. and suffixed with $ending. and suffixed with $ending. 'method'. http. # excerpt excerpt(string $haystack. The query string or keywords can be shown within the resulting document... # highlight highlight(string $haystack. int $radius=100.") Extracts an excerpt from $haystack surrounding the $needle with a number of characters on each side determined by $radius. string $needle. echo $this->Text->highlight($last_sentence. All URLs and emails are linked appropriately given the supplied $htmlOptions. . echo $this->Text->excerpt($last_paragraph. 50). Output: mined by $radius. This method is especially handy for search results. # autoLink autoLink(string $text.. ftp. This method is especially handy for search results.com</a> # autoLinkUrls autoLinkUrls(string $text. string $needle. '. green. array $options) Cuts a string to the $length and adds a suffix with 'ending' if the text is longer than $length. 'html' => false ) echo $this->Text->truncate( 'The killer crept forward and tripped on the rug.. indigo and violet # truncate truncate(string $text. Output: red. 22. int $length=100. yellow. all of which are optional: array( 'ending' => '.'. $options is used to pass all extra parameters. 'exact' => true. echo $this->Text->toList($colors). the truncation will occur after the next word ending. 'exact' => false ) ). . # toList toList(array $list.Output: Highlights $needle in $haystack <span class="highlight">using</span> the $highlighter string specified. and has the following possible keys by default. //The killer crept. blue. $and='and') Creates a comma-separated list where the last two items are joined with ‘and’. If 'html' is passed as true.. If 'exact' is passed as false. array( 'ending' => '. orange.... # stripLinks stripLinks($text) Strips the supplied $text of any HTML links..'. html tags will be respected and will not be cut off. Pasarle un texto "20081231" creará un resultado indeseado ya que tratará de convertirlo a segundos. Ago 21 1970. la convertirá a un entero. Si el $range es true. Este ayudante tiene dos tareas principales que puede realizar 1. ¡Puede dar formato a textos de tiempo 2.1 Formatting fromString( $date_string ) fromString toma una cadena de texto y la convierte en un objeto de tiempo. toAtom( $date_string ) toAtom devuelve una texto de tiempo en el formato Atom "2008-01-12T00:00:00Z" toRSS( $date_string ) toRSS devuelve un texto de tiempo en el formato RSS "Sat.# trim trim() An alias for truncate. Si la cadena suministrada es un número. 12 Jan 2008 00:00:00 -0500" .12 Tiempo El ayudante de tiempo (Time Helper). siendo este el número de segundos que han transcurrido desde el Epoch de Unix (1 de Enero 1970 00:00:00 GMT). lo sentimos). 7. 06:07" toQuarter( $date_string. Puede probar el tiempo (pero no pude doblarlo. lo que resultará en este caso "Vier.12. devolverá un arreglo con dos elementos con las fechas de inicio y fin en el formato "2008-03-31" toUnix( $date_string ) toUnix es un sinónimo para fromString. 3 o 4 dependiendo de en qué trimestre del año la fecha se encuantra. $range = false ) toQuarter devolverá 1. 2. como su nombre lo indica. te ayuda a ahorrar tiempo. Permite que se haga un procesamiento rápido de la información relacionada con el tiempo. 7. pero solo necesita un único objeto de tiempo timeAgoInWords( $date_string. 19:25". 19:25". niceShort( $date_string = null ) niceShort toma un texto de tiempo y lo devuelve en el formato "Jan 1st 2008. $field_name ) daysAsSql devuelve una cadena de texto en el formato "($campo >= '2008-01-21 00:00:00') AND ($campo <= '2008-01-25 23:59:59')". $end. 19:25". por defecto "+1 month" relativeTime( $date_string. devolverá en el formato "Ayer. Opción Descripción format un formato de fechas. 3 días". por defecto "on 31/12/08" determina el el punto de corte en el que no uará más palabras y usará el formato de fechas en end su lugar. $backwards = null ) timeAgoInWords toma una cadena de texto que representa una fecha y lo convierte a un formato amigable como "Hace 3 semanas. dayAsSql( $date_string. Jan 1st 2008. $field_name ) dayAsSql crea una cadena de texto en el mismo formato que daysAsSql. Si la fecha es el día actual el formato será "Hoy. format( $format = 'd-m-Y'.nice( $date_string = null ) nice toma una texto de tiempo y lo devuelve en el formato "Tue. . daysAsSql( $begin. $format = 'j/n/y' ) relativeTime es un básicamente un sinónimo para timeAgoInWords. Si la fecha ayer. $options = array(). gmt( $date_string = null ) gmt devolverá la fecha como un entero fijado al tiempo medio de Greenwich (GMT). 19:25". lo que devolverá el formato "el 31/12/08". Pasarle true en $backwards hará que se declare el tiempo en el futuro. $date_string) format es un sinónimo para la función date de php. 19:25 Enero 1 Hoy. Esto es usado .Función nice niceShort daysAsSql dayAsSql Formato Mar.1 serialize El método serialize toma un arreglo y crea una cadena XML de los datos. 3 semanas. minutos. 19:25 19:25 Ayer. $date_string ) wasWithinLast toma un intervalo de tiempo que es un texto en el formato "3 months" y acepta un intervalo de tiempo en segundos. 2 días relativeTime gmt 7. horas. se usará por defecto días 7.13.12. Enero 1 2008.2 Testing Time • • • • • • • isToday (es Hoy) isThisWeek (es esta Semana) isThisMonth (es este MEs) isThisYear (es este Año) wasYesterday (fue Ayer) isTomorrow (es Mañana) wasWithinLast (sucedió dentro del rango de tiempo) Todas las funciones anteriores devuelve true o false al pasarle una cadena de texto que represente una fecha. semanas. 19:25 ($campo >= '2008-01-21 00:00:00') AND ($campo <= '2008-01-25 23:59:59') ($campo >= '2008-01-21 00:00:00') AND ($campo <= '2008-01-21 23:59:59') el 21/01/08 Hace 7 minutos Hace 2 segundos 1200787200 timeAgoInWords Hace 3 mese. 2008. años. Si un intervalo de tiempo no es reconocido (por ejemplo se tipeó erróneamente). wasWithinLast toma el parámetro adicional $time_interval (intervalo de tiempo): $time->wasWithinLast( $time_interval. meses. 7.13 XML El helper XML simplifica la salida de documentos XML. días. $attrib = array(). quizás quieras invocar la clase XML directamente.2 elem El método elem permite construir una cadena-nodo XML con atributos y también contenido interno. 7. // genera: <?xml version="1. <?php echo $xml->header(array('version'=>'1. mixed $content = null. array('cdata'=>true. <?php echo $xml->header(). y usar el método toString de la misma. // genera: <myNameSpace:count>contenido</count> Si quieres empaquetar tu nodo de texto con CDATA. 'contenido').1')).13.3 header El método header() se usa para escribir la declaración de XML. el tercer argumento debería ser un arreglo con dos llaves: 'cdata' y 'value' echo $xml->elem('count'.'value'=>'contenido'). string elem (string $name. array('namespace' => 'myNameSpace'). // genera: <?xml version="1. Si necesitas más control sobre la serialización. $endTag = true) echo $xml->elem('count'. // El formato será similar a: // <model_name id="1" field_name="content" /> ?> El método serialize actua como un atajo para instanciar la clase XML incorporada en CakePHP. // genera: <count><![CDATA[contenido]]></count> 7.13. null. <?php echo $xml->serialize($data).0" encoding="UTF-8" ?> ?> Puedes entregar el número de la versión y tipo de codificación como parámetros del metodo header.1" encoding="UTF-8" ?> ?> .comúnmente para serializar datos de modelos. Person. People. 'geshi'). but will not contain an underscore. people . Ejemplo: Inflector::pluralize('ejemplo') retorna "ejemplos". 8. siendo accedida normalmente de forma estática.php')). App::import('Vendor'. some_thing. someThing apple_pie. UserProfileSetting. Oranges. Set y HttpSocket.8 Librerias de utilidades del núcleo CakePHP incluye librerías de utilidades de propósito general que se pueden invocar desde cualquier lugar de la aplicación. Person apples. Orange. App::Import('Model'. user_profile_settings. you can accomplish a lot. Por ejemplo. some_thing humanize apple_pie.named. App::import('Vendor'. people_person ApplePie. Person.2. 'flickr/flickr').2 Inflector La clase Inflector toma una cadena y puede manipularla produciendo variaciones en las palabras tales como pluralizacion y notacion de CaMeLlo.1 App App is a very small utility library. 8. Man camelize Apple_pie. applePie. 'SomeName'.'Post'). You can read more about it in the book or the API documentation 8.'File'). People. 'WellNamed'. Orange. Man Apples. App::import('Vendor'.DS. Men Apple.name. Oranges. People Person tableize Apple. Men singularize Apples. But. people_person Apple Pie. with the import method. App::import('Vendor'. // examples App::Import('Core'. Words underscore that contains spaces will be lower-cased. It only contains the import method. array('file' => 'some.'well. Some Thing. array('file' => 'services'. PeoplePerson It should be noted that underscore will only convert camelCase formatted words. SomeThing.1 Class methods Input Output pluralize Apple. some_thing.php')). $separator = '.3 Cadenas (String) La clase String incluye métodos para crear y manipular cadenas cómodamente.'. userResult. apple purée apple_puree 8. people_people apples. UserProfileSetting. El uuid es una cadena de 128 bits con el formato 485fc381-e790-47a3-9794-1337c0a8fe68. The slug method expects UTF-8 encoding. String::insert('Mi nombre es :nombre y tengo :edad años de edad. $options = array()) El método insert se utiliza para sustituir las claves de una plantilla (basada en una cadena de caracteres) por los valores de una matriz asociativa. String::uuid(). Estos métodos se acceden normalmente de forma estática. $data." .3. people Apple.1 uuid El método uuid se utiliza para generar identificadores únicos de acuerdo a la especificación RFC 4122. ignorando cualquier instancia de $separator que aparece entre $leftBound y $rightBound. $leftBound = '('.3.classify variable slug apples. user_result. Person apples. array('nombre' => 'Bob'. // 485fc381-e790-47a3-9794-1337c0a8fe68 8. 8. $rightBound = ')') Divide una cadena en subcadenas (tokens). peoplePeople Slug converts special characters into latin versions and converting unmatched characters and spaces to underscores.3.2 tokenize string tokenize ($data. user_profile_settings. // genera: "Mi nombre es Bob y tengo 65 años de edad. 'edad' => '65')). 8. por ejemplo: String::uuid().'.3 insert string insert ($string. y convertir en una cadena de caracteres. . alrededor de los marcadores. A partir de este momento.4 Xml Con la clase Xml podemos generar y analizar cómodamente fragmentos y documentos XML. íntegra en PHP. en función de la clave 'clean' de $options. $xml = new Xml($input). Con el ejemplo anterior podemos hacer lo siguiente: echo $xml->children[0]->children[0]->name. '> <container> <element id="first-el"> <name>My element</name> <size>20</size> </element> <element> <name>Your element</name> <size>30</size> </element> </container>'. sólo necesita que la extensión Xml/Expat esté instalada. los espacios en blanco y las marcas innecesarias que no sustituye Set::insert. // outputs 'element' . 8.1 Análisis Xml Para analizar un documento con la clase Xml.4 cleanInsert string cleanInsert ($string. 8. necesitamos una cadena con el Xml que queremos analizar. pero también está disponible html. El método que se utiliza por defecto es texto. $input = '<' . '?xml version="1. Esto crea un objeto de tipo documento Xml.3. $options = array()) Limpia la cadena String::insert de acuerdo a las opciones de la matriz $options.8.4. manipular. Esta solución. el objeto se puede leer.0" encoding="UTF-8" ?' . El objetivo de esta función es sustituir. Se puede llamar a la clase Set de CakePHP desde cualquier modelo o controlador. 'asc').1 Set-compatible Path syntax The Path syntax is used by (for example) sort.name'. del mismo modo que se llama a Inflector. CakePHP ofrece un conjunto muy útil de utilidades estáticas en la clase Set.echo $xml->children[0]->children[0]->children[0]->children[0]->value. Para esto.5. //outputs 'first-el' 8.Person. 1 => array('Shirt' => array('color' => 'black')) ). 8. poderosa y puede ayudar a construir un código más optimizado y elegante. '{n}. $result = Set::sort($a. Usage example (using Set::sort()): $a = array( 0 => array('Person' => array('name' => 'Jeff')).5 Set Gestionar correctamente las matrices puede ser una herramienta muy útil. // outputs 'My Element' echo $xml->children[0]->child('element')->attributes['id']. /* $result now looks like: Array ( [0] => Array ( [Shirt] => Array ( [color] => black ) ) [1] => Array ( [Person] => Array ( [name] => Jeff ) ) ) */ . Por ejemplo. Set::combine(). and is used to define a path. $result = Set::insert($a. Any string enclosed in brackets (besides {n} and {s}) is interpreted as a regular expression.2 insert array Set::insert ($list. $result = Set::insert($a. This section needs to be expanded. 8. $data = null) Inserta $data en un arreglo segun es definido en $path. 'pages. you can see which options are available. /* $result ahora queda: Array . In the table below. /* $result ahora queda como: Array ( [pages] => Array ( [name] => page ) [files] => Array ( [name] => files ) ) */ $a = array( 'pages' => array('name' => 'page') ).As you can see in the example above.5. $path. $a = array( 'pages' => array('name' => 'page') ). Expression {n} {s} Foo {[a-z]+} Definition Represents a numeric key Represents a string Any string (without enclosing brackets) is treated like a string literal. array('name' => 'files')).name'. others not. some things are wrapped in {}'s. array()). 'files'. vars'. /* $result ahora queda como: Array ( [pages] => Array ( [0] => Array ( [name] => main ) [1] => Array ( [name] => about [vars] => Array ( [title] => page title ) ) ) ) */ . $result = Set::insert($a.1. array('title' => 'page title')). 1 => array('name' => 'about') ) ).( [pages] => Array ( [name] => Array ( ) ) ) */ $a = array( 'pages' => array( 0 => array('name' => 'main'). 'pages. $path. 1 => array('Shirt' => array('color' => 'black')) ). '{n}. 'asc').5.Person. 'asc'). /* $result ahora queda: Array ( [0] => Array ( [Person] => Array ( [name] => Jeff . $a = array( 0 => array('Person' => array('name' => 'Jeff')). $dir) Ordena un arreglo según cualquier valor.name'.3 sort array Set::sort ($data. determinado por una ruta compatible con Set.Shirt'.8. '{n}. $result = Set::sort($a. /* $result ahora queda: Array ( [0] => Array ( [Shirt] => Array ( [color] => black ) ) [1] => Array ( [Person] => Array ( [name] => Jeff ) ) ) */ $result = Set::sort($a. array(3.5). '{n}'.1).4. array(3. 'desc').) ) [1] => Array ( [Shirt] => Array ( [color] => black ) ) ) */ $result = Set::sort($a. ). .6. /* $result ahora queda: Array ( [0] => Array ( [Shirt] => Array ( [color] => black ) ) [1] => Array ( [Person] => Array ( [name] => Jeff ) ) ) */ $a = array( array(7.2.4). // Null $result = Set::reverse(false). // false $a = array( 'Post' => array('id'=> 1. 'title' => 'First Post'). /* $result ahora queda: Array ( [0] => Array ( [0] => 3 [1] => 2 [2] => 1 ) [1] => Array ( [0] => 3 [1] => 4 [2] => 5 ) [2] => Array ( [0] => 7 [1] => 6 [2] => 4 ) ) */ 8.4 reverse array Set::reverse ($object) Set::reverse es básicamente el opuesto de Set::map. Si $object no es un objeto. reverse simplemente retornará $object. '{n}. $result = Set::reverse(null).$result = Set::sort($a. 'Comment' => array( .5.{n}'. 'asc'). Convierte un objeto en un arreglo. 'title' => 'Second Comment') ). 'title' => 'First Comment').array('id'=> 1. array('id'=> 2. ). 'title' => 'First Tag'). // Convierte $a en un objeto de clase /* $map ahora queda como: stdClass Object ( [_name_] => Post [id] => 1 [title] => First Post [Comment] => Array ( [0] => stdClass Object ( [id] => 1 [title] => First Comment ) [1] => stdClass Object ( [id] => 2 [title] => Second Comment ) ) [Tag] => Array ( [0] => stdClass Object ( [id] => 1 [title] => First Tag ) [1] => stdClass Object ( [id] => 2 [title] => Second Tag . 'title' => 'Second Tag') ). $map = Set::map($a). 'Tag' => array( array('id'=> 1. array('id'=> 2. ) ) ) */ $result = Set::reverse($map). /* $result ahora queda como: Array ( [Post] => Array ( [id] => 1 [title] => First Post [Comment] => Array ( [0] => Array ( [id] => 1 [title] => First Comment ) [1] => Array ( [id] => 2 [title] => Second Comment ) ) [Tag] => Array ( [0] => Array ( [id] => 1 [title] => First Tag ) [1] => Array ( [id] => 2 [title] => Second Tag ) ) . User.) ) */ $result = Set::reverse($a['Post']).id'. 'group_id' => 2. $groupPath = null) Crea un arreglo asociativo usando un $path1 como la ruta para construir las llaves.User. $result = Set::combine($a.Data'). Si $path2 no es especificado. $path2 = null. '{n}. $result = Set::combine(''. todos los valores serán inicializados como null (lo cual es útil para Set::merge). 'group_id' => 1. $result = Set::combine(array(). // $result == array(). 'Data' => array('user' => 'phpnut'. /* $result ahora queda como: .Data').'name' => 'The Gwoo')))).5.5 combine array Set::combine ($data. array('User' => array('id' => 14. '{n}. y opcionalmente $path2 como la ruta para obtener los valores.iglesias'. '{n}. '{n}. // $result == array().User. Opcionalmente se pueden agrupar los valores obtenidos según la ruta especificada en $groupPath. // Sólo retorna un arreglo /* $result ahora queda como: Array ( [id] => 1 [title] => First Post ) */ 8.User. $path1 = null. array('User' => array('id' => 25. $a = array( array('User' => array('id' => 2.id').'name' => 'Mariano Iglesias'))). 'Data' => array('user' => 'gwoo'. Masters'))). 'name' => 'Larry E. 'Data' => array('user' => 'mariano. '{n}.User. 'group_id' => 1.id'. non-existant').Data'). /* $result ahora queda como: Array ( [2] => [14] => [25] => ) */ $result = Set::combine($a.User.Array ( [2] => [14] => [25] => ) */ $result = Set::combine($a. Masters ) [25] => Array ( [user] => gwoo [name] => The Gwoo ) ) */ . '{n}. '{n}.iglesias [name] => Mariano Iglesias ) [14] => Array ( [user] => phpnut [name] => Larry E. '{n}.id'. '{n}. /* $result ahora queda como: Array ( [2] => Array ( [user] => mariano.User.id'.User.User. User.id'. '{n}. '{n}.name'). /* $result ahora queda como: Array ( [1] => Array ( [2] => Array ( [user] => mariano.$result = Set::combine($a.User.User.id'.User.Data'. /* $result ahora queda como: Array ( [2] => Mariano Iglesias [14] => Larry E.Data. '{n}. '{n}. '{n}. Masters [25] => The Gwoo ) */ $result = Set::combine($a.group_id').iglesias [name] => Mariano Iglesias ) [25] => Array ( [user] => gwoo [name] => The Gwoo ) ) [2] => Array ( [14] => Array ( [user] => phpnut [name] => Larry E.User. Masters ) ) ) */ . User. '{n}.id').iglesias [name] => Mariano Iglesias ) [14] => Array ( [user] => phpnut . '{n}.Data.group_id'). /* $result ahora queda como: Array ( [1] => Array ( [2] => Mariano Iglesias [25] => The Gwoo ) [2] => Array ( [14] => Larry E.id'.User.$result = Set::combine($a.User. '{n}. '{n}.name'.User. /* $result ahora queda como: Array ( [2] => [14] => [25] => ) */ $result = Set::combine($a.User. Masters ) ) */ $result = Set::combine($a.Data').id'. '{n}. /* $result ahora queda como: Array ( [2] => Array ( [user] => mariano.User. '{n}. /* $result ahora queda como: Array ( [1] => Array ( [2] => Array ( [user] => mariano.User.[name] => Larry E. Masters [25] => The Gwoo ) */ $result = Set::combine($a. '{n}.iglesias [name] => Mariano Iglesias ) [25] => Array ( [user] => gwoo [name] => The Gwoo ) ) [2] => Array ( .Data.name').User.id'. '{n}. '{n}.User.group_id'). '{n}.id'.User.Data'. Masters ) [25] => Array ( [user] => gwoo [name] => The Gwoo ) ) */ $result = Set::combine($a. /* $result ahora queda como: Array ( [2] => Mariano Iglesias [14] => Larry E.User. '{n}. '{n}.Data.Data. '{n}.Data.User. '{n}.User.group_id').user'.User.group_id'). array('{0}: {1}'. '{n}.id'.iglesias: Mariano Iglesias [25] => gwoo: The Gwoo ) [2] => Array ( [14] => phpnut: Larry E. Masters .name'. Masters ) ) ) */ $result = Set::combine($a. /* $result ahora queda como: Array ( [1] => Array ( [2] => Mariano Iglesias [25] => The Gwoo ) [2] => Array ( [14] => Larry E. /* $result ahora queda como: Array ( [1] => Array ( [2] => mariano. '{n}.User. Masters ) ) */ $result = Set::combine($a.User.User.[14] => Array ( [user] => phpnut [name] => Larry E.id'. '{n}. '{n}.User.name'). User.User.User.user'.name').Data. .User. '{n}. Masters: phpnut] => 14 [The Gwoo: gwoo] => 25 ) */ $result = Set::combine($a.Data.User. array('{0}: {1}'. '{n}.Data.id'). /* $result ahora queda como: Array ( [mariano. '{n}.User. array('%1$s: %2$d'. '{n}. array('%2$d: %1$s'. /* $result ahora queda como: Array ( [mariano.iglesias: Mariano Iglesias] => 2 [phpnut: Larry E.name').) ) */ $result = Set::combine($a. array('{1}: {0}'. /* $result ahora queda como: Array ( [Mariano Iglesias: mariano.Data. '{n}. '{n}.Data.User.iglesias: 2] => Mariano Iglesias [phpnut: 14] => Larry E. '{n}. '{n}. Masters] => 14 [gwoo: The Gwoo] => 25 ) */ $result = Set::combine($a.id').user'.user'.Data. Masters [gwoo: 25] => The Gwoo ) */ $result = Set::combine($a. '{n}.iglesias] => 2 [Larry E.User.user'.User.User.id').Data. '{n}.name'). $b = array('Cacheable' => array('enabled' => false). $trim = true) Normaliza un string o arreglo lista.Data. 'image_3_id'.6 normalize array Set::normalize ($list.name'). 'CounterCache'. 'Transactional').5.id'). $result = Set::normalize($a). 'Validator'. '{n}. /* $result ahora queda como: Array ( [Tree] => [CounterCache] => [Upload] => Array ( [folder] => products [fields] => Array ( [0] => image_1_id [1] => image_2_id [2] => image_3_id . 'Limit'.iglesias] => Mariano Iglesias [14: phpnut] => Larry E. 'image_2_id'. 'fields' => array('image_1_id'. 'Bindable'. $a = array('Tree'. 'image_4_id'.User. $sep = '. $assoc = true.User. Masters [25: gwoo] => The Gwoo ) */ 8. 'Upload' => array( 'folder' => 'products'.'{n}. /* $result ahora queda como: Array ( [2: mariano.'. 'image_5_id'))). $b). /* $result ahora queda como: Array ( [Cacheable] => Array ( [enabled] => ) [Limit] => [Bindable] => [Validator] => [Transactional] => ) */ $result = Set::merge($a. // Ahora mezclamos ambos y luego normalizamos /* $result ahora queda como: Array ( [0] => Tree [1] => CounterCache [Upload] => Array ( [folder] => products [fields] => Array ( [0] => image_1_id [1] => image_2_id [2] => image_3_id [3] => image_4_id [4] => image_5_id ) .[3] => image_4_id [4] => image_5_id ) ) ) */ $result = Set::normalize($b). $b)).) [Cacheable] => Array ( [enabled] => ) [2] => Limit [3] => Bindable [4] => Validator [5] => Transactional ) */ $result = Set::normalize(Set::merge($a. /* $result ahora queda: Array ( [Tree] => [CounterCache] => [Upload] => Array ( [folder] => products [fields] => Array ( [0] => image_1_id [1] => image_2_id [2] => image_3_id [3] => image_4_id [4] => image_5_id ) ) [Cacheable] => Array( [enabled] => ) [Limit] => [Bindable] => [Validator] => [Transactional] => ) */ . '2'.1' => '1. '3' => array('3. $result = Set::countDim($data). // $result == 1 $data = array('1' => '1.1')).1. true).1' => '3.1'). // $result == 2 $data = array('1' => array('1. '2'.1. '2'. '3' => array('3. '2'.1' => array('3.1' => array('3.1'.1'))).1. $result = Set::countDim($data).1' => '3. $all = false. // $result == 2 $data = array('1' => '1. $count = 0) Cuenta las dimensiones de un arreglo.1. '3' => array('3.1. true). $result = Set::countDim($data. '2'. '3' => array('3. 'three'). '2'. $result = Set::countDim($data).1' => '3.1' '2. $result = Set::countDim($data). '3').1'.1').1. $result = Set::countDim($data).1'.1.1' => => array('2.1.8.1' => '3' '1.1' => '3. // $result == 1 $data = array('1' => '1.1' => '1.1.1.1. // $result == 2 $data = array('1' => array('1.1'). // $result == 3 $data = array('1' => => array('1.1. => array('2' => => array('2.1'))).1' array('3.1'). '3' => array('3.1.5. array('3.1.1' .1' => '3.1')).1.1')). '2'. // $result == 1 $data = array('1' => array('1. $result = Set::countDim($data.1.1'))).1.1' => '1. $data = array('one'. Si $all está seteado como falso (su valor por defecto) sólo se considerarán las dimensiones del primer elemento en el arreglo.7 countDim integer Set::countDim ($array = null. false.1' => => '1.1. true).1.1.1.1. 1 => array('name' => 'about') ).1. $val2 = null) Calcula la diferencia entre un Set y un arreglo.5.1' $result = Set::countDim($data.1.1' array('3.1. o dos arreglos $a = array( 0 => array('name' => 'main').1.1' => '1.1' => '3.1' => => array('2. array('3.1.1. true). // $result == 5 8.1. true).8 diff array Set::diff ($val1.1'))).1.1. dos Sets.1' array('2.1.1.1.1'))).1.1. . // $result == 5 $set = array('1' => => array('1. array('3.1.1'). // $result == 5 $data = array('1' => => array('1.1.1.1.1' => => '1.1' => => array('2. '3' => array('2' => => array('2. $result = Set::countDim($data. 0). 1 => array('name' => 'about'). // $result == 4 $data = array('1' => => array('1.1' '3.1.1' array('3. $result = Set::countDim($set.1. array('2' '3' => => array('2.1. // $result == 2 $result = Set::countDim($set. $result = Set::countDim($data.1'). array('2.1.1' => => array('2.1' array('2. array('2' '3' => => array('2.1' '2.1.1.1'))).1')))).'3.1')))).1')))). 2 => array('name' => 'contact') ).1' '2.1' array('3. $b = array( 0 => array('name' => 'main'). array('3.1').1' => '3.1'))). true). $result = Set::diff($a. $b). /* $result ahora queda: Array ( [0] => Array ( [name] => main ) [1] => Array ( [name] => about ) [2] => Array ( . $b). /* $result ahora queda: Array ( [0] => Array ( [name] => main ) [1] => Array ( [name] => about ) ) */ $result = Set::diff(array(). /* $result ahora queda: Array ( [2] => Array ( [name] => contact ) ) */ $result = Set::diff($a. array()). [name] => contact ) ) */ $b = array( 0 => array('name' => 'me'). // $result == array('My Index 1' => array('First' => 'The first item')) $set = array( 'My Index 1' => array('First' => array('Second' => array('Third' => . $result = Set::check($set. $result = Set::diff($a.9 check boolean Set::check ($data. $path = null) Verifica si una ruta particular está seteada en un arreglo $set = array( 'My Index 1' => array('First' => 'The first item') ). // $result == True $result = Set::check($set. array()). 'My Index 1. 1 => array('name' => 'about') ). /* $result ahora queda: Array ( [0] => Array ( [name] => main ) ) */ 8.5. $b). 'My Index 1'). // $result == True $result = Set::check($set.First'). Third').Fourth').Seconds.array('Fourth' => 'Heavy.First. // $result == True $result = Set::check($set. $path = null) Elimina un elemento de un Set o arreglo según sea definido en una ruta en la variable $path.Second.10 remove boolean Set::remove ($list.5.Third. 'My Index 1. $result = Set::remove($a. 'My Index 1. $result = Set::check($set.')))) ). => array('name' => 'files') .First.First. 'My Index 1. // $result == False 8.Third. 'My Index 1. Nesting.Second'). // $result == True $result = Set::check($set.Fourth'). $a = array( 'pages' 'files' ). array('name' => 'files')). 'files'. // $result == True $result = Set::check($set.Second. /* $result ahora queda como: Array ( [pages] => Array ( [name] => page ) ) */ => array('name' => 'page').First. Article. // $result == "Article 2" $result = Set::extract($a.8. '{n}.title'). '{n}. array('Article' => array('id' => 2.{[a-z]+}" . $path = null) Obtiene un valor desde un arreglo u objeto que está contenido en una ruta entregada usando una sintáxis de ruta de arreglo.id'). 'title' => 'Article 1')). '3. array('Article' => array('id' => 3.Article.11 classicExtract array Set::classicExtract ($data. 'title' => 'Article 3'))). /* $result ahora queda: Array ( [0] => 1 [1] => 2 [2] => 3 ) */ $result = Set::extract($a. $result = Set::extract($a. /* $result ahora queda: Array ( [0] => Article 1 [1] => Article 2 [2] => Article 3 ) */ $result = Set::extract($a.Article.title').title').Donde "{n}" representa una llave numérica.5. '1.Article. 'title' => 'Article 2')).// $result == null . es decir: • "{n}.Person. "Person" representa una cadena literal • "{[a-z]+}" (es decir. cualquier string literal encerrado en llaves junto a {n} y {s}) es interpretado como una expresión regular. Ejemplo 1 $a = array( array('Article' => array('id' => 1. /* $result ahora queda como: .{s}. 'test' => array(array('name' => 'jippi')). /* $result ahora queda como: Array ( [0] => Array ( [0] => page ) [1] => Array ( [0] => fruit ) ) */ $result = Set::extract($a.Ejemplo 2 Plain Text View $a = array( 0 => array('pages' => array('name' => 'page')).'{\w+}. 'dot. /* $result ahora queda como: Array ( [0] => Array ( [0] => jippi ) [1] => Array ( [0] => jippi ) ) */ $result = Set::extract($a.name').name').test' => array(array('name' => 'jippi')) ).name'). 1 => array('fruites'=> array('name' => 'fruit')).{n}.{\w+}. $result = Set::extract($a. '{s}. '{n}. Array ( [0] => Array ( [pages] => page ) [1] => Array ( [fruites] => fruit ) [test] => Array ( [0] => jippi ) [dot. /* $result Ahora queda como: Array ( [0] => Array .'{\d+}.name').{\w+}.test] => Array ( [0] => jippi ) ) */ $result = Set::extract($a.name'). /* $result ahora queda como: Array ( [0] => Array ( [pages] => page ) [1] => Array ( [fruites] => fruit ) ) */ $result = Set::extract($a.'{n}.{\w+}. name').'{s}'). /* $result ahora queda como: Array ( [0] => Array ( [0] => Array ( [name] => jippi ) ) [1] => Array ( [0] => Array ( .'{s}.{\d+}.( [pages] => page ) [1] => Array ( [fruites] => fruit ) ) */ $result = Set::extract($a. /* $result ahora queda como: Array ( [0] => Array ( [0] => jippi ) [1] => Array ( [0] => jippi ) ) */ $result = Set::extract($a. [name] => jippi ) ) ) */ $result = Set::extract($a.{n}').test] => Array ( [0] => Array ( [name] => jippi ) ) ) */ . /* $result ahora queda como: Array ( [test] => Array ( [0] => Array ( [name] => jippi ) ) [dot.'{[a-z]}').test] => Array ( [0] => Array ( [name] => jippi ) ) ) */ $result = Set::extract($a.test}. /* $result ahora queda como: Array ( [dot. '{dot\. // returns false $res=Set::matches('/Article[id=2]'. $i = null. $a[1]['Article']). $res=Set::matches(array('id>2').8. 'id<3'. $a). array('Article' => array('id' => 3.12 matches boolean Set::matches ($conditions. // returns true $res=Set::matches(array('3'). // returns true $res=Set::matches(array('id<2'). // returns true $res=Set::matches(array('id>=3'). // returns true $res=Set::matches(array('non-existant'). 'title'). $a[1]['Article']). 'title' => 'Article 2')). $a[1]['Article']). $a[1]['Article']). // returns false $res=Set::matches(array('id>=2'). array('Article' => array('id' => 2. // returns true $res=Set::matches(array('id'. 5). $a[1]['Article']). null. // returns true $res=Set::matches(array('id>1'. $a = array( array('Article' => array('id' => 1. $a[1]['Article']). $a). $length=null) Set::matches puede ser usado para ver su un item o una ruta calza con ciertas condiciones. 'title' => 'Article 3'))). $data=array(). // returns true $res=Set::matches('/Article[id=4]'. // returns true $res=Set::matches(array('id').5. $a). // returns false $res=Set::matches(array('id>1'). $a[1]['Article']). // returns true . // returns false $res=Set::matches(array(). $a[1]['Article']). // returns false $res=Set::matches(array('id<=2'). null. $a[1]['Article']). // returns true $res=Set::matches(array('5'). 'title' => 'Article 1')). $a[1]['Article']). 3). 'id!=0'). User. // results retorna: // array(1..[:last] /Comment/. Esta función permite extraer datos rápidamente sin tener que hacer un ciclo a través de un arreglos multidimensionales o de estructuras de árbol. $results = Set::extract('/User/id'.[:first] /Comment[text=/cakephp/i] Descripción Similar al clásico {n}.id Selecciona el nombre del segundo User Selecciona todos los Users con un id < 2 Selecciona todos los Users con un id > 2 pero < 5 Selecciona los nombres de todos los Posts que tienen al menos un comentario escrito por john Selecciona todos los Post que tienen la llave 'title' Selecciona el contenido del primer comentario Selecciona el último comentario Selecciona el primer comentario Selecciona todos los comentarios que tienen un texto que calce con la expresión regular (regex) /cakephp/i /Comment/@* Selecciona todos los nombres clave de todos los comentarios Actualmente..8..5.php. $data=null.0 para retornar subconjuntos de la data resultante de un find().. Si $path es un arreglo o $data es vacío la llamada se redirigirá a la función Set::classicExtract.test.[1] /Comment/.5.2. $users). // Uso común: $users = $this->User->find("all"). . y también las sugerencias para futuras funcionalidades son bien recibidas. Para aprender más acerca de Set::extract refiérase a la función testExtract() en /cake/tests/cases/libs/set./name /Posts[title] /Comment/.4. $options=array()) Set::extract utiliza la sintáxis básica XPath 2. Por favor reporte cualquier bug que encuentre en ellas. Los seleccionadores implementados actualmente son: Seleccionador /User/id /User[2]/name /User[id<2] /User[id>2][<5] / Post/Comment[author_name=jo hn]/. solo las rutas absolutas con un solo '/' están soportadas.3.).13 extract array Set::extract ($path. Nate [1] => Masters. 'something' => '{0}')). '{0}. Garrett ) */ $res /* Array ( [0] => Boston.Person. .Person. $format. TN [2] => Venice Beach. = Set::format($data. 'last_name' => 'Masters'. 'something' => '{1}'))). {1}}'. 'something' => '42')). '{n}.5. = Set::format($data. CA ) */ $res /* Array ( = Set::format($data. 'city' => 'Venice Beach'. '{n}. $keys) Returns a series of values extracted from an array. '{1}.state')). 'state' => 'TN'.Person. 'last_name' => 'Woodworth'. 'state' => 'CA'.state')). array('Person' => array('first_name' => 'Garrett'. 'last_name' => 'Abele'.Person.city'.14 format array Set::format ($data.first_name'. array('{n}. 'city' => 'Boondock'. $res /* Array ( [0] => Abele.city'. '{{0}. formatted in a format string.Person. array('Person' => array('first_name' => 'Larry'.Person. {1}'. '{n}. MA [1] => Boondock.8. {0}'. array('{n}. array('{n}. 'city' => 'Boston'. $data = array( array('Person' => array('first_name' => 'Nate'. 'state' => 'MA'. Larry [2] => Woodworth.last_name')). Nate [1] => 0. '{n}.first_name'. %1$s'. 0 ) */ = Set::format($data.something'. '{%2$d. array('{n}.something')).something')).Person. array('{n}.Person. MA} [1] => {Boondock. Larry [2] => 0.Person.something')).first_name'. '{n}.Person. '%1$s. Garrett ) */ $res /* Array ( [0] => Nate. = Set::format($data. '{n}.Person. .[0] => {Boston. array('{n}. '%2$d. CA} ) */ $res /* Array ( [0] => {42. 42} [1] => {0. TN} [2] => {Venice Beach. {0}} [2] => {0. 0 [2] => Garrett. = Set::format($data. %1$s}'.Person. %2$d'. 42 [1] => Larry. {1}} ) */ $res /* Array ( [0] => 42. // $res is 'two' $res = Set::enum('no'. two'). 'yes' => 1)).5.15 enum string Set::enum ($select.5. 'yes' => 1). 'second' => 'two')). $res = Set::numeric($data). array('first' => 'one'. // $res is false $data = array('one'). // $res is true $data = array(1 => 'one'). If a comma separated $list is passed arrays are numeric with the key of the first being 0 $list = 'no. . 1 => 'yes'). $list defaults to 0 = no 1 = yes if param is not passed $res = Set::enum(1. $list=null) The enum method works well when using html select elements.8. keys can be strings example: array('no' => 0. array('no' => 0. It returns a value from an array list if the key exists. $res = Set::numeric(array_keys($data)). yes' would translate to $list = array(0 => 'no'. If an array is used. // $res is 'one' 8. $res = Set::numeric($data). 'one. // $res is 0 $res = Set::enum('first'.16 numeric boolean Set::numeric ($array=null) Checks to see if all the values in the array are numeric $data = array('one'). 'two'. 'four'. // $res is true $data = array('1' => 'one'. 4 => 'four'. 'a' => 'five'). 3 => 'three'. 4 => 'four'. 3 => 'three'. // $res is true $data = array(1 => 'one'.// $res is false $data = array('one' => 'two'). // $res is true $data = array('one'. $res = Set::numeric(array_keys($data)). $res = Set::numeric($data). $res = Set::numeric(array_keys($data)). 5 => 'five'). // $res is true $data = array(0). 2 => 'two'. 5 => 'five'). $res = Set::numeric(array_keys($data)). 2 => 'two'. 'five'). // $res is false $data = array('one' => 1). // $res is true $data = array('one'. 'three'. 3 => 'three'. // $res is false . $res = Set::numeric($data). 4 => 'four'. $res = Set::numeric(array_keys($data)). $res = Set::numeric($data). 2 => 'two'. Basically. 'hash' => '68a9f053b19526d08e36c6a9ad150737933816a5'. array( "IndexedPage" => array( "id" => 2. ). 'created' => "1195055503". however you can map values into any type of class. $data = array( array( "IndexedPage" => array( "id" => 1. ) ). 'updated' => "1195055503". $tmp = 'stdClass') This method Maps the contents of the Set object to an object hierarchy while maintaining numeric keys as arrays of objects. 'get_vars' => ''. By default it turns an array into a stdClass Object. 'updated' => "1195055503". ) ). 'created' => "1195055503". the map function turns array items into initialized class objects. 'redirect' => ''. $mapped = Set::map($data).17 map object Set::map ($class = 'stdClass'. 'get_vars' => ''. /* $mapped now looks like: Array .com/'. Example: Set::map($array_of_values.5. 'redirect' => ''. "url" => 'http://blah.8. "url" => 'http://blah. 'hash' => '68a9f053b19526d08e36c6a9ad150737933816a5'.com/'. 'nameOfYourClass'). com/ [hash] => 68a9f053b19526d08e36c6a9ad150737933816a5 [get_vars] => [redirect] => [created] => 1195055503 [updated] => 1195055503 ) ) */ Using Set::map() with a custom class for second parameter: class MyClass { function sayHi() { echo 'Hi!'. //Now you can access all the properties as in the example above.com/ [hash] => 68a9f053b19526d08e36c6a9ad150737933816a5 [get_vars] => [redirect] => [created] => 1195055503 [updated] => 1195055503 ) [1] => stdClass Object ( [_name_] => IndexedPage [id] => 2 [url] => http://blah. } } $mapped = Set::map($data.( [0] => stdClass Object ( [_name_] => IndexedPage [id] => 1 [url] => http://blah. //but also you can call MyClass's methods $mapped->[0]->sayHi(). 'MyClass'). . 'field_one'=>'a1. array("extra_field")). 'field_two'=>'a1.m1.f2')). 'field_one'=>'a3. $array2) This function merges two arrays and pushes the differences in array2 to the bottom of the resultant array.f2 [field_three] => a3. Example 1 $array1 $array2 = = array('ModelOne' array('ModelOne' => => array('id'=>1001.f1'.5.f2'. $array2 = array("b"=>"b". 'field_two'=>'a3.m1. "c"=>"string". $res = Set::pushDiff($array1. /* $res now looks like: Array ( [ModelOne] => Array ( [id] => 1001 [field_one] => a1.8. /* $res now looks like: Array ( [a] => b [1] => 20938 [c] => string [b] => b [3] => 238 [4] => Array ( [0] => extra_field ) ) */ . 1 => 20938.m1. "c"=>"string"). $array2).m1.m1. 'field_three'=>'a3.m1. $array2).f1 [field_two] => a1.m1.f1'. 3 => 238.18 pushDiff array Set::pushDiff ($array1.m1.f3')).f3 ) ) */ Example 2 $array1 = array("a"=>"b". $res = Set::pushDiff($array1. array('id'=>1003. 20 merge array Set::merge ($arr1. 'Te digo'. false))). . pero no lo hace si las llaves que contienen strings. true. $isArray=null) Filtra los elementos vacíos de una ruta de arreglo. $res = Set::filter(array('0'. array('una cosa'. excluyendo el cero '0'. /* $res now looks like: Array ( [0] => 0 [2] => 1 [3] => 0 [4] => Array ( [0] => una cosa [1] => Te digo [2] => es asi [3] => ) ) */ 8. 0. Verifica el test de unidad para mas información. $arry1 = array( array( 'id' => '48c2570e-dfa8-4c32-a35e-0d71cbdd56cb'. false. a diferencia de array_merge. 'name' => 'mysql raleigh-workshop-08 < 2008-09-05.5.5.sql '. 'es asi'. Esta función trabaja sobre un numero ilimitado de argumentos y hace un casting como arreglos a los argumentos que no lo sean. La diferencia esta en que si un arreglo de llaves contiene otro arreglo entonces la función se comporta recursivamente. ambas de PHP.19 filter array Set::filter ($var. a diferencia de array_merge_recursive. $arr2=null) Esta función se puede considerar como un híbrido entre las funciones array_merge y arraymerge_recursive.8. $arry2 = 4. $res = Set::merge($arry1. $arry3 = array(0=>"Arreglo de prueba". ) ). /* $res ahora queda como: Array ( [0] => Array ( [id] => 48c2570e-dfa8-4c32-a35e-0d71cbdd56cb [name] => mysql raleigh-workshop-08 < 2008-09-05. $arry3).sql [description] => Importing an sql dump ) [1] => Array ( [id] => 48c257a8-cf7c-4af2-ac2f-114ecbdd56cb [name] => pbpaste | grep -i Impago | pbcopy [description] => Eliminar las lineas que dicen "Impago". 'name' => 'pbpaste | grep -i Impago | pbcopy'. "gatos"=>"perros"). array( 'id' => '48c257a8-cf7c-4af2-ac2f-114ecbdd56cb'. ) [2] => 4 [3] => Arreglo de prueba [gatos] => perros ) */ . 'description' => 'Eliminar las lineas que dicen "Impago".'. $arry2.'description' => 'Importing an sql dump' ). $b = array( 0 => array('name' => 'main').7.6 Security The security library handles basic security measures such as providing methods for hashing and encrypting data. // True 8. // False $result = Set::contains($b.1 Cache::read() Cache::read($key. // True $result = Set::contains($a. $config = null) El método Cache::read() se utiliza para leer el valor guardado en el cache con el nombre $key de la configuración $config.7 Cache La clase Cache en CakePHP provee una interfaz generica con varios metodos de cache. $b).21 contains boolean Set::contains ($val1. 2 => array('name' => 'contact'). $a = array( 0 => array('name' => 'main'). 'a' => 'b' ).8. $result = Set::contains($a. $a). . Es posible tener varias configuraciones y motores en el archivo app/config/core. Si $config es null se utiliza el configuración por defecto. 1 => array('name' => 'about') ). $val2 = null) Determines if one Set or array contains the exact keys and values of another.5. 8.php 8. $a). 1 => array('name' => 'about'). Puedes leer o borrar ese valor más adelante refiriéndote a él por $key. $config = null) Cache::delete() te permitirá eliminar completamente cualquier objeto que tengas guardado en el Cache. if ($cloud !== false) { return $cloud. Por ejemplo: $cloud = Cache::read('cloud'). 8.Cache::read() retornará el valor almacenado si es un cache valido o false si el cache expiró o no existe.3 Cache::delete() Cache::delete($key.7. Cache::write() puede almacenar cualquier tipo de objeto y es ideal para guardar resultados de búsquedas de modelos. así que asegúrate de usar el operador de comparación estricta === o !==. .. Los contenidos del cache podrían evaluarse como false. Cache::write('posts'. Puedes especificar opcionalmente una configuración para guardar el cache. $cloud).2 Cache::write() Cache::write($key. $config = null).. } Usa Cache::write() y Cache::read() para reducir con facilidad la cantidad de viajes a la base de datos para recuperar registros. $value. Si no se especifica $config se usará la configuración por defecto. 8. return $cloud. $posts).7. if (($posts = Cache::read('posts')) === false) { $posts = $this->Post->find('all'). Cache::write() escribirá un valor $value en el Cache. // store data in cache Cache::write('cloud'. } // generate cloud data // . Añadiendo el código anterior en tu app/config/core. 'duration'=> '+1 week'. deberías usar Cache::set() antes de leer los datos más tarde. DS. 'duration'=> '+1 hours'. array( 'engine' => 'File'. Debes especificar qué motor usar. Si no haces eso. se usará la configuración por defecto cuando se lea la clave de la cache. 'probability'=> 100. rutas. // long Cache::config('long'. 'path' => CACHE . 8. Si usas Cache::set() para cambiar los ajustes para una escritura.7. 'path' => CACHE. No se pone por defecto a File. Esto eliminará la necesidad de llamar a Cache::set(). Usar configuraciones múltiples te puede ayudar a reducir la cantidad de veces que necesitas usar Cache::set() así como centralizar todos tus ajustes de Cache.4 Cache::config() Cache::config() se usa para crear configuraciones de Cache adicionales. o prefijos de los que tiene tu configuración por defecto. Cache::set(array('duration' => '+30 days')). Cache::config('short'. // Later on Cache::set(array('duration' => '+30 days')).5 Cache::set() Cache::set() te permite puentear temporalmente los ajustes de configuración de Cache para una operación (normalmente read o write). $results = Cache::read('results').php tendrás dos configuraciones de Cache adicionales. array( 'engine' => 'File'. . )). Si usar repetidamente Cache::set() quizás deberías crear una nueva configuración de Cache. El nombre de estas configuraciones 'short' o 'long' se usa como parámetro $config para Cache::write() y Cache::read(). Cache::write('results'. 'prefix' => 'cake_short_' )). Estas configuraciones a mayores pueden tener diferente duración.7. $data). 'long' . motores.8. . 8. string get($uri. delete). $query.8 HttpSocket CakePHP includes an HttpSocket class which can be used easily for making requests.8.1 get The get method makes a simple HTTP GET request returning the results. $results = $HttpSocket->get('http://www.3 request The base request method. 'param2' => 'bar'). either in string form: "param1=foo¶m2=bar" or as a keyed array: array('param1' => 'foo'.com/add'. $results = $HttpSocket->post('www. 'q=cakephp').8. $query is any query string parameters. //$results contains what is returned from the post.8. App::import('Core'. 'HttpSocket'). array('name' => 'test'. string function post ($uri. 8. $data. $query is the data to be posted. $HttpSocket = new HttpSocket(). 'param2' => 'bar').somesite.8. Returns the results of the request. $HttpSocket = new HttpSocket(). which is called from all the wrappers (get. either in string form: "param1=foo¶m2=bar" or as a keyed array: array('param1' => 'foo'. 'HttpSocket'). $request) $uri is the web address where the request is being made. App::import('Core'. put. post. $request) The parameters for the post method are almost the same as the get method. such as those to web services. 'type' => 'user')). $uri is the web address where the request is being made. //returns html for Google's search results for the query "cakephp" 8.com/search'.google.2 post The post method makes a simple HTTP POST request returning the results. 'User-Agent' => 'CakePHP' ). Here is the format and default settings: var $request = array( 'method' => 'GET'. 'pass' => null ). 'version' => '1. 'header' => array( 'Connection' => 'close'. 'user' => null. 'fragment' => null ). 'line' => null. 'raw' => null. 'user' => null. 'cookies' => array() ). 'pass' => null. . 'path' => null. 'query' => null. 'auth' => array( 'method' => 'Basic'. 'uri' => array( 'scheme' => 'http'. 'host' => null.1'.string function request($request) $request is a keyed array of various options. 'body' => ''. 'port' => 80. La consola Bake es otro esfuerzo para tener CakePHP corriendo rápido. 9. vistas y controladores. La consola Bake puede crear cualquiera de los ingredientes básicos de CakePHP: modelos. . asegúrate de que tienes PHP CLI instalado y que además tienes los módulos necesarios habilitados(Ej: MySQL).9 Aplicaciones de Consola Principales CakePHP viene con muchas aplicaciones de consola por defecto. te pedirá que crees un archivo de configuración para la base de datos. si todavía no creaste uno./cake bake.1 Generación de Código con Bake Ya has aprendido sobre scaffolding en CakePHP: una forma simple de armar una aplicación con sólo una base de datos y algunas clases básicas. Si tienes algún problema con el script. podrías necesitar darle permisos de ejecución al script bash o llamarlo usando. Y no estamos hablando sólo de clases estructurales: Bake puede crear una aplicación completamente funcional en sólo unos minutos. Bake es el paso natural que toman las aplicaciones una vez que han pasado por la etapa de scaffolding Para utilizar Bake necesitar ejecutar el script localizado en el directorio /cake/console/ $ cd . La consola de Cake corre usando PHP CLI (command line interface). y otras son de uso general para ayudarte a producir más rápido Esta sección explica cómo usar las aplicaciones de consola que vienen empaquetadas con CakePHP Antes de que sigas con esta sección. Cuando utilices Bake por primera vez. tal vez quisieras ver La consola de CakePHP. Algunas de ellas se utilizan en combinacion con algunas de las funcionalidades de Cake. (ejemplo ACL o i18n). así que si nunca la has usado anteriormente. echale un vistazo. De hecho. La configuración de la consola no está cubierta por este capítulo./cake/console/ $ cake bake Dependiendo de tu configuración. . • Plugin support has been greatly improved. and a number of features and enhancements have been built in. and made easier to understand.3 For 1. • Questions have been clarified. puedes ejecutar cualquiera de estos comandos directamente de la línea de comandos: $ cake bake db_config $ cake bake model $ cake bake view $ cake bake controller $ cake bake project 9. • Two new tasks (FixtureTask and TestTask) are accessible from the main bake menu • A third task (TemplateTask) has been added for use in your shells.3 bake has had a significant overhaul. cuando ejecutes Bake te presentará estas opciones: --------------------------------------------------------------App : app Path: /ruta/al/proyecto --------------------------------------------------------------Interactive Bake Shell --------------------------------------------------------------[D]atabase Configuration [M]odel [V]iew [C]ontroller [P]roject [Q]uit What would you like to Bake? (D/M/V/C/P/Q) > Alternativamente. You can use either -plugin PluginName or Plugin.Una vez que hayas configurado la base de datos.class.1 Bake improvements in 1. • All the different bake tasks now allow you to use connections other than default for baking.1. Using the -connection parameter. we'll take some time to look at some of the new commands. New bake command New commands have been added to make baking easier and faster. Controller. TemplateTask is a behind the scenes task. a ParentThread and ChildThread association will be created. baked tests are now attempt to find as many fixtures as possible. Model. With 1. TestTask and TemplateTask. and it handles file generation from templates. but using the interactive options or the -records option you can enable fixture generation using live data. This allows you to re-run them and regenerate tests and fixtures at any point in your development process.3 we've separated out Fixture and Test making them separate tasks. • Self Associated models using parent_id are now detected. new parameters and updated features. that builds everything at once and makes speedy rebuilds easy. • Baked Tests include as many fixtures as they know about.3 almost all the content in the files generated by bake are controlled by templates and the TemplateTask. New FixtureTask. For 1. In addition to being rebuildable at any time. Test cases also generate skeleton test methods for every non-inherited public method in your classes. and fixtures could only be generated when baking models.• Multiple validations on models has been added. • Fixtures and Tests can be baked separately. including plugin detection (plugin detection does not work on PHP4). Saving you one extra step. So with the laundry list of features. In the past getting into testing often involved fighting through numerous 'Missing Table' errors. View baking all feature an all subcommand. With more advanced fixture detection we hope to make testing easier and more accessible. The FixtureTask not only generates fixtures with dummy data. Fixture and test baking were a bit of a pain in the past. but all other code was not. For example if your model is named Thread. . In previous versions of CakePHP baked views were template based. You could only generate tests when baking the classes. This made adding tests to your applications later or even regenerating fixtures with new schemas a bit painful. using -admin will allow you to bake views for actions that begin with Routing. Class should be an existing object of the chosen type. cake/console/templates/default/ to see what directories and files are required of a bake theme. Parameters on the ControllerTask have changed as well. In 1. An example plugin theme path called for blue_bunny bake would You theme be can would placed look be in at app/plugins/bake_theme/vendors/shells/templates/dark_red/.2 baked views used templates that could be changed to modify the view files bake generated. if your bake theme doesn't implement a template. ViewTask has had an -admin flag added. In 1. However.cake bake model all Would bake all the models for an application in one shot. An app bake app/vendors/shells/templates/blue_bunny. cake bake fixture all will regenerate all the basic fixtures for your application. test cases. . models. As well as more templates. you can also have multiple template sets or.3 is the addition of more templates.2. You can use cake bake test <type> <class> to create test cases for already created objects in your app. There are separate templates for controllers. bake themes.3 templates are used to generate all output from bake. and have several subcommands each. 'model'. 'helper'. Type should be one of the standard CakePHP types ('component'. The -count parameter allows you to set the number of fake records that are created. other installed themes will be checked until the correct template is found. like view files. cake bake bake controller all would bake all controllers and cake bake view all would generate all controller scaffold is now cake bake controller public. 'behavior') but doesn't have to be. controller action sets. Similarly cake view files.admin As mentioned before cake bake fixture and cake bake test are new. and the view files from 1. or as part of plugins. By running fixture task interactively you can generate fixtures using the data in your live tables. Bake themes can be provided in your app. Templates Galore New in bake for 1. fixtures. 'controller'. Para forzar a que cree un esquema de todas las tablas. Para reconstruir el esquema de la base de datos a partir de un archivo schema. debes añadir la opción -f en la línea de comandos. for example will allow you to use interactive bake to add controllers to your Todo plugin. así como crear y restaurar instantáneas de base de datos.3 bake will find which of the pluginPaths the named plugin is located on. and add the files there. cake bake controller -plugin Todo. In 1. 9. Additional / multiple plugin paths are supported as well.Posts and cake bake controller Posts -plugin Todo. Puedes generar un archivo de esquema de la base de datos usando: $ cake schema generate Esto generará un archivo llamado schema. sin importar en qué motor se vaya a implementar.1 Generando y Usando Archivos de Esquemas Un archivo de esquema permite transportar fácilmente el esquema de la base de datos. New in 1. cake bake controller Todo. La aplicación SchemaShell procesará sólo las tablas para las cuales existe un modelo definido. In addition to cake bake plugin Todo controller Posts.3 are additional ways to specify plugin names when using bake.2 Gestión del Esquema de la BBDD y Migraciones El SchemaShell proporciona la funcionalidad para crear un esquema de objetos.php generado anteriormente.Additional plugin support. there are two new forms. volcados del esquema sql. The plugin parameter can be while using interactive bake as well.php en tu directorio app/config/sql. 9. debes ejecutar: $ cake schema run create .2. In the past bake required your plugin to be in app/plugins. 2 Migrations with CakePHP schema shell Migrations allow for versioning of your database schema. You can then restore to any of these schema files at any time by running: . so that as you develop features you have an easy and database agnostic way to distribute database changes.2. 9..php and so on. el volcado será mostrado por pantalla sin ser escrito en ningún archivo. Para generar un archivo SQL que contenga las sencencias CREATE TABLE ejecuta: $ cake schema dump volcado. Migrations are achieved through either SCM controlled schema files or schema snapshots. [O]verwrite [S]napshot [Q]uit Would you like to do? (o/s/q) Choosing [s] (snapshot) will create an incremented schema..php. it will create schema_2.php. Versioning a schema file with the schema shell is quite easy.php.Esto borrará y volverá a crear todas las tablas basándose en el contenido del archivo schema.sql es el nombre que deseas ponerle al volcado.sql Donde volcado. Schema file exists. If you already have a schema file created running $ cake schema generate Will bring up the following choices: Generating Schema. Los archivos de esquema pueden ser usado para genera volcados de SQL. Si omites este nombre. So if you have schema. 10 Deployment Steps to deploy on a Hosting Server. Cópialos en: app/vendors/shells/templates/views 4. Dirígete a: cake/console/libs/templates/views 2. . The schema shell will prompt you to confirm you wish to perform the ALTER statements that represent the difference between the existing database the currently executing schema file. 9. sigue los siguientes pasos: 1. You can perform a dry run by adding a -dry to your command.$ cake schema update -s 2 Where 2 is the snapshot number you wish to run. Existen cuatro archivos ahí 3. Haz los cambios a la salida HTML para controlar la forma en que "bake" construye las vistas.3 Modificando el HTML producido por defecto del script bake Si deseas modificar el HTML producido por el comando "bake". Cuanto más programación orientada a objetos hayas hecho. No te preocupes.11 Ejemplo de Aplicación En esta sección podrás ver cómo se unen todas las piezas en una aplicación de CakePHP típica. Obtendremos e instalaremos Cake. puedes visitar CakeForge y The Bakery para encontrar aplicaciones existentes y otros componentes. necesitarás un conocimiento básico del patrón MVC. y creando luego la lógica necesaria para mostrar. Finalmente. es menos de una página. Esto es lo que necesitas: 1. creando la base de datos y configurándola. Un servidor de base de datos. Aquí asumiremos que estás usando Apache. Deberás saber lo suficiente de SQL como para crear una base de datos: Cake se encarga del resto a partir de ahí. Este tutorial te guiará para que puedas crear un blog simple. aunque las instrucciones para utilizar otros servidores deberían ser similares. Alternativamente. Conocimiento básico de PHP. 4. añadir. 2. Comencemos! . Un servidor web. 11. Tal vez necesitemos jugar un poco con la configuración del servidor. 3. mejor. Es nuestro deseo incrementar la productividad y hacer la programación más agradable: esperamos que notes esto a medida que vayas avanzando con el tutorial.1 Blog Bienvenidos a CakePHP! Probablemente estás viendo este tutorial porque deseas aprender más acerca de cómo funciona Cake. pero la mayoría de pueden obtener y poner en marcha Cake sin modificar la configuración para nada. Puedes darle un vistazo al capítulo "Comenzando con CakePHP". editar y borrar posts del blog. pero no tengas miedo si eres un fan de la programación procedural. en la sección: Entendiendo Modelo-Vista-Controlador. Nosotros utilizaremos MySQL es este tutorial. sección : Estructura de archivos de CakePHP.cakephp.x.php Este es un buen momento para aprender un poco más acerca de cómo Cake utiliza la estructura de directorios: puedes verlo en el capítulo "Principios básicos de CakePHP".11. Ejecuta el siguiente código SQL en tu base de datos: /* Primero.2 Creando la Base de Datos del Blog A continuación debemos crear la base de datos en la que se basará nuestro blog. title VARCHAR(50). También insertaremos algunos posts para poder utilizarlos de testeo.1.org/projects/cakephp/ y descargar la versión estable.1. crear la tabla para los posts: */ CREATE TABLE posts ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY.org/repo/trunk/cake/1. Para 1. los directorios deberían lucir algo así: /ruta_al_directorio_raiz /app /cake /docs /vendors . Para este tutorial vamos a usar la https://svn.2. Una vez terminado. body TEXT. 11. modified DATETIME DEFAULT NULL ). deber colocar los archivos en el directorio raíz del servidor (DocumentRoot). Lo primero que haremos será crear una tabla para almacenar nuestros posts.x/ Sin importar cómo hayas conseguido Cake.htaccess index.1 Obteniendo Cake Primero debemos conseguir una copia reciente de CakePHP. created DATETIME DEFAULT NULL.2.x También puedes obtener una copia mediante svn en esto debes visitar la sección del proyecto CakePHP en Cakeforge http://cakeforge.x. . created) VALUES ('Título ataca de nuevo'. 'Y el cuerpo del post a continuación. La elección de los nombres de la tabla y las columnas no es arbitrario. éstos serán manejados automáticamente por Cake. sin embargo. 'Esto es realmente exitante! No. y al tener los campos 'modified' y 'created'. Haz una copia en el mismo directorio. INSERT INTO posts (title.default. 'host' => 'localhost'. El archivo de configuración debería ser fácil de seguir: sólo debes reemplazar los valores en el arreglo $default con la información que se corresponda a tu configuración.'.created) VALUES ('El título'./* Luego insertar algunos posts de ejemplo: */ INSERT INTO posts (title. INSERT INTO posts (title. NOW()).'. Puedes ver las "Convenciones de CakePHP" para más información. . esta será la primera y última vez que configuren algo. 11.3 Configuración de la Base de Datos en Cake Vamos a decirle a Cake dónde está nuestra base de datos y cómo conectarse a ella.created) VALUES ('Un título otra vez'.body. NOW()).'.php. tendrás la posibilidad de aprovechar muchas ventajas y evitar la configuración. Un ejemplo completo deberíe verse como el siguiente: var $default = array( 'driver' => 'mysql'.php. Una copia del archivo de configuración de la base de datos se encuentra en /app/config/database.1. Cake es lo suficientemente flexible como para acomodarse inclusive al peor esquema de base de datos de aplicaciones antiguas.body. Si sigues las convenciones de Cake relacionadas a la base de datos. pero nombrándola database. Para muchos. NOW()). y las convenciones relacionadas a los nombres de las clases (puedes consultar ambas en "Convenciones de CakePHP"). 'persistent' => 'false'. si utilizas las convenciones ahorrarás mucho tiempo de desarrollo. 'Este es el cuerpo del post. pero es suficiente con decir que al llamar a la tabla 'posts' automáticamente estará asociada con el modelo Post.body. La mejor forma de hacer esto es encontrar con qué nombre de usuario está corriendo el servidor (<? php echo `whoami`. El comando a ejecutar (en sistemas *nix) puede lucir similar a esto: $ chown -R www-data app/tmp . ?>) y cambiar el propietario de app/tmp a ese usuario. La cadena de seguridad (o "salt") es usada para generar hashes. */ Configure::write('Security.php. 'prefix' => ''.'port' => ''. 'password' => 'c4k3-rUl3Z'.4 Configuración Opcional Hay dos ítems más que pueden ser configurados. La mayoría de los desarrolladores realiza estos pasos. pero para este tutorial no son necesarios. 'login' => 'usuario_de_la_BD'. 'pl345e-P45s_7h3*S@l7!').salt'.php. ?> La segunda tarea es darle al servidor web permisos de escritura sobre el directorio app/tmp. No importan tanto cuál es el nuevo valor del salt. <?php /** * Una cadena aleatoria usada en los métodos de hashing de seguridad. 'database' => 'tutorial_blog'. El segundo ítem es darle acceso de escritura a Cake.1. 'schema' => ''. Una vez que hayas guardado el nuevo database. a su directorio tmp. deberías poder abrir el navegador en la página de inicio de Cake y ver que puede conectarse a la base de datos sin problemas. siempre y cuando no sea fácil de adivinar. El primero es definir una cadena (o "salt") para darle más seguridad a los hash. Puedes cambiarla editando el archivo /app/config/core. 'encoding' => '' ). 11. En /app/config/core. Por razones de seguridad y de performance.conf correcto en vez de un httpd. 11. descomenta la línea que diga algo como: Configure::write('App. podrás ver un aviso cuando te encuentres navegando la aplicación en modo debug.' como ocultos.baseUrl'. Asegúrate que la sobreescritura (override) esté permitida (allowed): en tu httpd.htaccess necesarios. Algunos consejos para que hacerlo funcionar: 1. 2. .5 Una aclaración para mod_rewrite Es muy probable que los usuarios novatos tengan problemas con mod_rewrite. Si la página de bienvenida de CakePHP se ve un poco rara (sin imágenes o estilos CSS). Asegúrate que estás editando el httpd. Esto a veces sucede porque algunos sistemas operativos tratan a los archivos cuyo nombre comienza con '.1. así que haremos una mención aquí.so o (en Apache 1. env('SCRIPT_NAME')).conf. Si no quieres o no puedes hacer funcionar mod_rewrite (o algún otro módulo compatible).Si por alguna razón CakePHP no puede escribir en ese directorio. no setees AllowOverride en All dentro de <Directory />. necesitarás usar las 'pretty' URLs proporcionadas por CakePHP.3) AddModule mod_rewrite.c en tu httpd. busca el bloque <Directory> que haga referencia al directorio de tu sitio web. A su vez.conf. Asegúrate que AllowOverride esté puesto en All para el Directorio correcto. 3. Asegúrate que tu copia de CakePHP proviene de la sección de descargas del sitio o desde nuestro repositorio SVN.. Asegúrate que Apache esté cargando mod_rewrite correctamente! Deberías ver algo como LoadModule rewrite_module libexec/httpd/mod_rewrite. deberías tener una sección en la que se definen los permisos sobre cada Directorio en tu servidor. Por una u otra razón. probablemente no tengas funcionando el módulo mod_rewrite en tu sistema.php. y no los copia. puedes haber conseguido una copia de CakePHP sin los archivos . 4.conf específico de usuario o sitio web. 11.e.com/controllername/actionname/param. Post. y suele ahorrarnos problemas con los nombres de las clases en PHP4. como prefijos de tabla. Nombrando nuestro modelo como Post. si no puede encontrar el archivo correspondiente en /app/models. CakePHP dinámicamente creará un objeto de modelo por ti. revisar el capítulo Models del Manual. . y el archivo que crearemos lo grabaremos en /app/models/post.example.php) CakePHP no reconocerá ninguna de tus configuraciones y usará las opciones por defecto. Siempre es una buena idea agregar la variable $name.1. editar.php. Esto también dice que si nombras incorrectamente tu archivo (i.htaccess Esto hará que tus URLs se vean de la en forma vez de www. Para más información sobre modelos. El archivo completo debería verse así: <?php class Post extends AppModel { var $name = 'Post'. y validación.6 Crear un modelo Post La clase Model es el pan y manteca de las aplicaciones CakePHP.htaccess /app/. Los archivos de clases de modelo de CakePHP van en la carpeta /app/models.example.php or posts.php/controllername/actionname/param www. tendremos la base para poder hacer luego nuestras acciones de vista. callbacks.htaccess /app/webroot/. agregar.com/index. y eliminar. CakePHP puede automáticamente inferir que este modelo será usado en el controlador PostsController.Also remove these . y será atado a la tabla de la base de datos llamada posts.htaccess files: /. } ?> La convención en la nomenclatura es muy importante en CakePHP. Creando un modelo CakePHP que interactúe con nuestra base de datos. com/posts/). Por ejemplo. El código para esa acción se vería como esto: <?php class PostsController extends AppController { var $name = 'Posts'. Puede tentarte querer nombrar a tus controladores y acciones de cierta forma para obtener cierto URL. Resiste la tentación. function index() { $this->set('posts'. si definimos una función llamada foobar(). Así es como debe verse un controlador básico: <?php class PostsController extends AppController { var $name = 'Posts'. De forma similar.example. } } ?> Déjenme explicar un poquito la acción.example. cuando los usuarios ingresan www.com/posts/foobar.example. crearemos un controlador para nuestros posts.php dentro del directorio /app/controllers. $this->Post->find('all')). los usuarios podrían acceder a ella en www.com/posts/index.7 Crear un controlador para Post A continuación.11. agreguemos una acción a nuestro controlador.com/posts/index (que es lo mismo que www. En pocas palabras.1. Definiendo la función index() en nuestro PostsController. esperan ver un listado de posts. los usuarios pueden ahora acceder a la lógica ubicada en www. } ?> Ahora. Sigue las convenciones de CakePHP (nombres en plural para los controladores. Puedes mapear luego . Ubicaremos este nuevo controlador en un archivo llamado posts_controller.example. Las acciones a menudo representan una función o una interfase en una aplicación.) y crea nombres legibles y entendibles para las acciones. es el lugar en el que juegas con los modelos y realizas el trabajo con los posts. etc. El controlador es donde existe toda la lógica del negocio para la interacción con los posts. La única instrucción en la acción usa set() para pasar datos desde el controlador a la vista (que crearemos a continuación). pero puede terminar usando XML.8 Creando las Vistas(Views) de los Post Ahora que tenemos los datos que fluyen a nuestro modelo y la lógica de nuestra aplicación y el flujo definido por nuestro controlador. La línea iguala la variable de vista llamada 'posts' al valor retornado por el método del modelo Post find('all').1. o incluso de datos binarios. Los Diseños (Layouts) de presentación son el código que se envuelve alrededor de las vista (views). Cake view(vistas) son solo fragmentos de presentaciones-sabrosas que se adaptan dentro de las aplicaciones diseñadas. ¿Recuerda que en la última sección la forma en que asigno la variable "posts" a de la vista fue usando método set()? La forma que transmite datos a la vista sería algo como esto: // print_r($posts) output: Array ( [0] => Array ( [Post] => Array ( [id] => 1 [title] => The title [body] => This is the post body. pero por ahora. chequea el capítulo "Desarrollando con CakePHP": "Controllers". vamos a crear una vista (view) para la acción “index” que hemos creado anteriormente. CSV. [created] => 2008-02-13 18:34:55 [modified] => ) . 11. vamos a usar el valor por defecto. Nuestro modelo Post está disponible automáticamente en $this->Post porque hemos seguido la convención de nombres de Cake. Para la mayoría de las aplicaciones estaremos mezclando HTML con PHP.URLs a tu código usando "routes". y pueden ser definidas y modificadas. Para aprender más sobre los controladores de Cake. ) [1] => Array ( [Post] => Array ( [id] => 2 [title] => A title once again [body] => And the post body follows. [created] => 2008-02-13 18:34:57 [modified] => ) ) ) Los archivos de las Cake’s views (vistas de cake) se almacenan en /app/views dentro de una carpeta con el nombre del controlador que corresponden (tendremos que crear una carpeta llamada "posts" en este caso). el código de nuestra vista podría ser algo como esto: <!-. [created] => 2008-02-13 18:34:56 [modified] => ) ) [2] => Array ( [Post] => Array ( [id] => 3 [title] => Title strikes back [body] => This is really exciting! Not. Para dar formato a los datos de los posts en un cuadro lindo.ctp --> <h1>Blog posts</h1> <table> <tr> <th>Id</th> <th>Title</th> <th>Created</th> </tr> .Archivo: /app/views/posts/index. De lo contrario. Usted puede haber notado el uso de un objeto llamado $html. Si le sucedió que hizo click en uno de los enlaces que hemos creado en esta vista (que vinculan el título de un post a una URL /posts/view/some_id). manejo JavaScript y Ajax. Al especificar las URL en Cake. Puede obtener más información sobre cómo utilizarlas en el capítulo "Built-in Helpers". imprimiendo la información de cada post--> <?php foreach ($posts as $post): ?> <tr> <td><?php echo $post['Post']['id']. Usted debe observar en la vista. el formato correcto con el título y la lista de los posts. probablemente ha sido informado por CakePHP que la acción aún no ha sido definida. es que algo ha ido mal.<!-. la forma de salida. en cuyo caso es muy astuto. sólo tiene que dar una ruta relativa de la base de la aplicación. usted debería ser capaz de escribir en el navegador http://www. ?></td> </tr> <?php endforeach. CakePHP viene con un conjunto de "view helpers" (vistas de ayuda) que hacen cosas como la vinculación. ?> </table> Creemos que esto debería ser algo sencillo. ?></td> <td> <?php echo $html->link($post['Post']['title']. pero lo que es importante señalar aquí es que el método link() generará un vínculo HTML con un título determinado (el primer parámetro) y la URL (la segunda parámetro). En este punto. Es así. que las URL se suelen quedar de la forma de /controlador/acción/parametro1/parametro2 (/controller/action/param1/param2).Aqui se hace el ciclo que recorre nuestros arreglo $posts . "/posts/view/". o que realmente ya la a definido. ?> </td> <td><?php echo $post['Post']['created'].com/posts/index. la vamos a crear ahora en el . Esta es una instancia de la clase CakePHP HtmlHelper.example.$post['Post']['id']). Si no recibió el informe. y cake llena en el resto. archivo: /app/views/posts/view. Si un usuario solicita /posts/view/3. } } ?> La llamada set() les debe lucir familiar. . } function view($id = null) { $this->Post->id = $id. <!-. entonces el valor '3' es pasado como $id. $this->Post->find('all')). $this->set('post'. $this->Post->read()).ctp.ctp --> <h1><?php echo $post['Post']['title']?></h1> <p><small>Created: <?php echo $post['Post']['created']?></small></p> <p><?php echo $post['Post']['body']?></p> Verifique que esto esta funcionando en los vínculos de /posts/index o manualmente solicitando un post accediendo a /posts/view/1. Tenga en cuenta que la acción de nuestra vista toma un parámetro: la ID del post que nos gustaría ver. Este parámetro se entrega a la acción a través de la URL solicitada.PostsController: <?php class PostsController extends AppController { var $name = 'Posts'. Informamos de que estamos usando read() en lugar de find('all') porque realmente solo queremos la información de un único post. function index() { $this->set('posts'. Ahora vamos a crear la vista para las “view” de nuestra nueva acción y lo colocaremos en /app/views/posts/view. En primer lugar. simplemente hacer que se quede en la vista. $this->Post->find('all')). Cuando un usuario utiliza un formulario de datos POST en su aplicación. pero debe habilitarse para agregar nuevos post’s. $this->Post->read()).11. } function view($id) { $this->Post->id = $id. no guarda. $this->set('post'. Si por alguna razón. esta información está disponible en $this->data.1. Esto nos da la oportunidad de mostrar los errores de validación de usuario u otras advertencias. $this->redirect(array('action' => 'index')).9 Agregando Posts Leer y mostrar de la base de datos nuestros post’s es un gran comienzo. Usted puede usar las funciones pr() o debug para imprimir. si .'). } function add() { if (!empty($this->data)) { if ($this->Post->save($this->data)) { $this->Session->setFlash('Your post has been saved. } } } } ?> Lo que esta acción add() hace es lo siguiente: si los datos del formulario presentado no están vacíos. trate de guardar los datos utilizando el modelo Post. empezar por crear la acción add() controlador PostsController: <?php class PostsController extends AppController { var $name = 'Posts'. function index() { $this->set('posts'. la acción index del controlador posts. echo $form->input('title'). El FormHelper está disponible por defecto en todas las vista en $form. Todo el mundo odia a la codificación de sus infinitos formularios y rutinas de validación. que muestra el mensaje y borra la variable de sesión correspondiente. Puede referirse a Router::url en función de la API para ver los formatos en los que se puede especificar una dirección URL para diversas funciones de cake. tendrás que utilizar FormHelper de Cake en tus vistas. echo $form->end('Save Post').ctp --> <h1>Add Post</h1> <?php echo $form->create('Post').quieres ver como luce esto. CakePHP hace que sea más fácil y más rápido. 11.File: /app/views/posts/add. echo $form->input('body'. ?> Aquí. nosotros usamos el FormHelper para generar la etiqueta de apertura de un formulario HTML. Usamos la función del componente Session setFlash() para adjuntar un mensaje a una variable de sesión que se mostrará en la página después de la redirección. Llamando al método save() haremos comprobación de errores de validación y abortar el guardado si algo ocurre. array('rows' => '3')). Aquí está el código HTML que genera $form->create() : <form id="PostAddForm" method="post" action="/posts/add"> . Para aprovechar las características de la validación. El parámetro array('action'=>'index) se traduce en la URL /posts es decir.1.10 Validación de Datos Cake lleva un largo camino recogiendo la monotonía de la validación de formularios de entrada. Hablaremos de cómo se manejan los errores en las siguientes secciones. La función redirect del controlador redirige a otra dirección URL. Esta es nuestra Vista Agregar(add view): <!-. En el diseño tenemos $session->flash() . El $form->end() genera una llamada al botón de enviar y termina el formulario. var $validate = array( 'title' => array( 'rule' => 'notEmpty' ). Antes de el <table>. añada la siguiente línea: <?php echo $html->link('Add Post'. consulte el Chapter "Built-in Helpers" para conocer más acerca de los helpers.array('controller' => 'posts'.ctp para incluir un nuevo enlace "Añadir entrada". 'action' => 'add'))?> Puede estar preguntarse: ¿cómo le digo a mi CakePHP sobre los requisitos de validación? Reglas de validación se definen en el modelo. El primer parámetro le dice a CakePHP a que campo corresponden. Hay un poco de introspección y automátizacion aquí: input() es la salida de diferentes elementos basados en el modelo del campo especificado. Ahora vamos a volver y actualizar nuestro /app/views/posts/index. Si una cadena se suministra como el primer parámetro a end(). Una vez más.en este caso. El método $form->input() es utilizado para crear elementos de formulario del mismo nombre. el número de filas para el textarea. y el segundo parámetro le permite especificar una amplia gama de opciones . 'body' => array( 'rule' => 'notEmpty' ) ). este supone que está construyendo un formulario que suministra datos a la acción add() (o a la acción edit() cuando el parámetro id esta incluído en los datos del formulario ($form->data)). FormHelper producirá un botón de enviar con ese nombre seguido del cierre de la etiqueta. Vamos a mirar hacia atrás en nuestro modelo y después haremos algunos ajustes: <?php class Post extends AppModel { var $name = 'Post'. } ?> .Si create() es llamado sin suministrarle parámetros. a través del metodo POST. 1. esta acción no tiene ninguna vista. El motor de validación de CakePHP es fuerte.'). Ahora que tiene las reglas de validación en su lugar.$id. $this->Session->setFlash('The post with id: '. ?></p> <table> <tr> <th>Id</th> <th>Title</th> <th>Actions</th> <th>Created</th> . vamos a crear un medio para que los usuarios eliminen post’s. etc) y la flexibilidad para añadir sus propias reglas de validación. Aquí. direcciones de correo electrónico.ctp <h1>Blog posts</h1> <p><?php echo $html->link('Add Post'.El arreglo $validate le dice a CakePHP cómo validar sus datos cuando se llama el método save() .' has been deleted. Como hemos utilizado el método input() del componente FormHelper para crear elementos de nuestro formulario. Es posible que desee actualizar su vista de índice (index) con vínculos que permitan a los usuarios eliminar posts. entonces: /app/views/posts/index. $this->redirect(array('action'=>'index')). y utiliza $this->Session->setFlash() para mostrar al usuario un mensaje de confirmación después de la reorientación a /posts. nuestros mensajes de error de validación se mostrará automáticamente.11 Borrando Posts A continuación. con una serie de normas pre-construidas (números de tarjetas de crédito. Para obtener más información sobre esta configuración. 11. utilice la aplicación para tratar de añadir un post con un título o el cuerpo vacío para ver cómo funciona. consulte el Capítulo Validación de Datos. he especificado que tanto el campos cuerpo y el título no deben estar vacía. Porque estamos sólo ejecutando una lógica y redireccionando. } Esta lógica es eliminar el post por $ id. Comenzaremos con la acción delete() en el PostsController: function delete($id) { $this->Post->delete($id). array('action' => 'add')). </tr> <!-Aquí esta el ciclo que muestra $posts a través de nuestro arreglo, imprimiendo la información de los posts --> <?php foreach ($posts as $post): ?> <tr> <td><?php echo $post['Post']['id']; ?></td> <td> <?php echo $html->link($post['Post']['title'], array('action' => 'view', 'id' => $post['Post']['id']));?> </td> <td> <?php echo $html->link('Delete', array('action' => 'delete', $post['Post']['id']), null, 'Are you sure?' )?> </td> <td><?php echo $post['Post']['created']; ?></td> </tr> <?php endforeach; ?> </table> El código de esta vista también utiliza HtmlHelper para que pregunte al usuario con un diálogo de confirmación JavaScript de antes de intentar borrar un post. 11.1.12 Editando Posts Editando Post: ¡aquí vamos! Eres un CakePHP pro por ahora, por lo que deberías haber adoptado un patrón. Hacer la acción, luego la vista. Aquí esta la acción edit() del PostsController que se vería como: function edit($id) { $this->Post->id = $id; if (empty($this->data)) { $this->data = $this->Post->read(); } else { if ($this->Post->save($this->data)) { $this->Session->setFlash('Your post has been updated.'); $this->redirect(array('action' => 'index')); } } } Esta acción primero chequea los datos del formulario para enviarlos. Si no fue enviado, este busca el post y se lo pasa a la vista. Si algunos datos se han enviado, intenta guardar los datos utilizando el modelo Post (o los rechazará y mostrara al usuario los errores de validación). La vista de edición puede tener un aspecto parecido a este: /app/views/posts/edit.ctp <h1>Edit Post</h1> <?php echo $form->create('Post', array('action' => 'edit')); echo $form->input('title'); echo $form->input('body', array('rows' => '3')); echo $form->input('id', array('type'=>'hidden')); echo $form->end('Save Post'); ?> Esta vista muestra el formulario de edición (con los valores de publicados), junto con los mensajes de errores de validación necesarios. Cabe destacar aquí: que CakePHP asumirá que usted está editando un registro si el campo 'id' está presente en el arreglo de datos. Si 'id' no está presente (mirar hacia atrás en nuestra opinión de añadir), Cake asumirá que usted está añadiendo un nuevo registro para llamar a save() Ahora puede actualizar su vista de indice, con enlaces para ir a editar posts específicos: /app/views/posts/index.ctp (edit links added) <h1>Blog posts</h1> <p><?php echo $html->link("Add Post", array('action'=>'add')); ?> <table> <tr> <th>Id</th> <th>Title</th> <th>Action</th> <th>Created</th> </tr> <!-- Aqui se hace el ciclo que recorre nuestros arreglo $posts , imprimiendo la información de cada post --> <?php foreach ($posts as $post): ?> <tr> <td><?php echo $post['Post']['id']; ?></td> <td> <?php </td> <td> <?php echo $html->link( 'Delete', array('action'=>'delete', 'id'=>$post['Post'] ['id']), null, 'Are you sure?' )?> <?php echo $html->link('Edit', array('action'=>'edit', 'id'=>$post['Post']['id']));?> </td> <td><?php echo $post['Post']['created']; ?></td> </tr> <?php endforeach; ?> </table> echo $html->link($post['Post'] ['title'],array('action'=>'view', 'id'=>$post['Post']['id']));?> 11.1.13 Rutas Para algunos, el enrutamiento por defecto de CakePHP funciona lo suficientemente bien. Los desarrolladores que son sensibles a la facilidad de uso y compatibilidad del motor de búsqueda general aprecian la forma en que CakePHP URL mapea acciones específicas. Así que vamos a hacer un cambio rápido a las rutas en este tutorial. Para obtener más información sobre las técnicas avanzadas de enrutamiento, consulte "Configuración de Rutas". Por defecto, CakePHP responde a una petición de la raíz de su sitio (es decir, http://www.example.com) con su PagesController, haciendo una vista llamada "home". En lugar de ello, vamos a sustituir esto con nuestros PostsController mediante la creación de una regla de enrutamiento. El enrutamiento de Cake se encuentra en /app/config/routes.php. Usted querrá comentar o eliminar la línea que define la ruta raíz predeterminada. El aspecto que presenta es: Router::connect ('/', array('controller'=>'pages', 'action'=>'display', 'home')); Esta línea conecta a la URL "/" con la página de inicio por defecto de CakePHP. Queremos que esto se conecte con nuestro propio controlador, por lo que añadiremos una línea que tiene que ver asi: Router::connect ('/', array('controller'=>'posts', 'action'=>'index')); Esto debe conectar a los usuarios que solicitan '/' a la acción índex() de nuestra pronto-a-sercreado PostsController. CakePHP también hace uso de "enrutamiento inverso" - si con la citada ruta que definió array('controller'=>'posts', 'action'=>'index') pasa a una función que espera un arreglo, la url resultante utilizada es '/'. Es, por tanto, una buena idea utilizar siempre los arreglos (arrays) de urls como rutas, esto significa definir a dónde va una url, y también se asegura de que los enlaces llevan al mismo sitio. 11.1.14 Conclusión Creado aplicaciones de esta manera ganará paz, honor, amor, y dinero, incluso más allá de sus fantasías más salvajes. Simple, ¿no? Tenga en cuenta que este tutorial es muy básico. CakePHP tiene muchas más características que ofrecer, y es flexible en formas que no se quiso cubrir aquí para simplificar. Utilice el resto de este manual como una guía para la construcción de aplicaciones con más ricas-características. Ahora que ha creado la base de una aplicación Cake está listo para algo real. Comience su propio proyecto, lea el resto del Manual y API. Si necesita ayuda, vengan a vernos en el #cakephp. Bienvenido a CakePHP! 11.2 Simple Acl controlled Application Bienvenido a CakePHP, si eres nuevo con CakePHP revisa primero el tutorial del Blog. Si ya lo has visto, y ya leiste acerca del uso de bake, y estás necesitando configurar un sistema Auth y Acl para el ingreso y autenticación de tus usuarios, entonces este es el tutorial para ti. Como ya mencionamos anteriormente este tutorial asume que ya tienes experiencia con CakePHP, y que estás familiarizado con todos los conceptos del MVC que constituyen el núcleo. También te sientes cómodo con el uso de la consola y el script bake. Si no conoces lo anterior, primero aprende todas estos elementos y luego vuelve. Este tutorial será mucho más facil de seguir y tendra más sentido para ti. En este tutorial usaremos AuthComponent y AclComponent. Si no sabes lo que son, revisa sus páginas en el manual antes de proceder. Que necesitas? 1. Un servidor web. Asumiremos que usas Apache, pero las instrucciones son similares en caso de otro servidor. Quizas debamos jugar un poco con la configuración del servidor, pero muchos consiguen utilizar CakePHP sin tener que configurar nada. 2. Un servidor de bases de datos. Usaremos mySQL en este tutorial. Necesitarás conocer suficiente SQL para crear la base de datos: CakePHP tomará las riendas de aquí en adelante. 3. Conocimiento PHP básico. Mientras mas programación orientada a objeto que hayas hecho mejor: pero si eres fanático de la programación procedural no tienes nada que temer. 11.2.1 Preparando nuestra aplicación Primero, consigamos una copia fresca de CakePHP. Para descargarla, visita la página de CakePHP en Cakeforge: http://cakeforge.org/projects/cakephp/ y descarga la última versión estable. Para este tutorial necesitaras la version 1.2.x.x También puedes hacer checkout/export desde el trunk en: https://svn.cakephp.org/repo/trunk/cake/1.2.x.x/ Una vez que tengas la copia de Cake, configuraremos el archivo database.php, y cambiemos ademas el valor de Security.salt en el archivo app/config/core.php. Desde acá construiremos un esquema de base de datos simple para nuestra aplicación. Ejecuta el siguiente código SQL en tu base de datos. CREATE TABLE users ( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, username VARCHAR(255) NOT NULL UNIQUE, password CHAR(40) NOT NULL, group_id INT(11) NOT NULL, created DATETIME, modified DATETIME ); CREATE TABLE groups ( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, created DATETIME, modified DATETIME ); CREATE TABLE posts ( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, user_id INT(11) NOT NULL, title VARCHAR(255) NOT NULL, body TEXT, created DATETIME, modified DATETIME ); CREATE TABLE widgets ( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, part_no VARCHAR(12), quantity INT(11) ); Estas serán las tablas que usaremos para construir el resto de la aplicación. Una vez que tengamos la estructura en la base de datos ya podemos comenzar a hornear. Usa cake bake para crear rapidamente tus modelos, controladores y vistas. Evite el uso de Scaffold(andamios) aquí. La generación de la ACOS se verán seriamente afectados si hornear los controladores con la función de Scaffold. Mientras hornees tus modelos Cake automagicamente detectara las asociaciones entre tus modelos (o relaciones entre las tablas). Dejemos que Cake entregue las asociaciones hasMany y belongsTo apropiadas. Si te pregunta por elegir hasOne o hasMany, en general necesitarás una relación hasMany (solo) para este tutorial. Dejaremos admin routing fuera por ahora, este tema ya es bastante complejo sin ellos. Asegurate además de NO agregar ninguno de los dos componentes a ningún controlador mientras estes horneándolo. Haremos eso tan pronto podamos. Ahora debieras tener modelos, controladores y vistas para tus tablas users (usuarios), groups (grupos), posts (artículos) y widgets. En modo actions (acciones), esto autenticará nuestros ARO que son los groups y users, con los objetos ACO - controladores & acciones. 11.2.2 Preparándose para agregar Autentificación (Auth) Ahora tenemos un funcionamiento de aplicación CRUD. Bake debe tener todas las configuraciones de las relaciones que necesitamos, si no es así debemos añadirlas ahora. Hay algunas otras piezas que añadiremos antes de que podamos agregar los componentes de autenticación y acl. En primer lugar añadir las acciones login(inicio sesión) y logout(salir sesión) en el controlador UsersController. function login() { //Autentificación Magica } function logout() { //limpio por ahora. } No es necesario preocuparse por agregar cualquier hash de contraseñas, debido a que AuthComponent lo hará por nosotros automáticamente al crear/editar los usuarios, y cuando se inicia sesión, una vez configurado correctamente. Además, si usted agrega un hash de las contraseñas manualmente AuthComponent no podrá iniciar sesión en absoluto. Pues los hash no coincidiran. A continuación tenemos que hacer algunas modificaciones a AppController. Si usted no tiene el controlador /app/app_controller.php, créelo. Tenga en cuenta que esto va en /app/ y no /app/controllers/. Dado que queremos que todo el sitio este con el control de autentificación y ACL, vamos a ponerlos en AppController. class AppController extends Controller { var $components = array('Acl', 'Auth'); function beforeFilter() { //Configure AuthComponent $this->Auth->authorize = 'actions'; $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); $this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login'); $this->Auth->loginRedirect = array('controller' => 'posts', 'action' => 'add'); } } Antes de configurar la ACL en todo tendrá que añadir algunos usuarios y grupos. Con AuthComponent en uso no podremos acceder a ninguna de nuestras acciones, ya que no se ha iniciado sesión. Ahora vamos a añadir algunas excepciones de manera que AuthComponent nos permitirá crear algunos grupos y usuarios. Por lo tanto en GroupsController y UsersController añadiremos lo siguiente. function beforeFilter() { parent::beforeFilter(); $this->Auth->allowedActions = array('*'); } Estas declaraciones son para decirle a AuthComponent que permita el acceso del público a todas las acciones. Esto es sólo temporal y se eliminará una vez que tengamos unos usuarios y grupos en nuestra base de datos. No agregue los usuarios o grupos, no todavía. 11.2.3 Inicializar las tablas Acl en la BD Antes de que creemos nuestros users o groups querremos conectarlos con el Acl. Sin embargo, en este punto aun no tenemos ninguna tabla Acl y si tratamos de ver cualquier página en este momento, obtendremos un error de tabla no encontrada. Para eliminar estos errores necesitamos ejecutar un archivo de esquema. En una consola ejecuta el siguiente codigo: cake schema run create DbAcl. Este esquema te preguntara acerca de eliminar y crear estas tablas. Acepta la eliminación y creación de éstas tablas. Recuerda especificar la ruta de tu directorio app si estás fuera de ella. 1. En tu directorio de aplicación app: $ /path/to/cake/console/cake schema run create DbAcl 2. Fuera de tu directorio de aplicación app: $ /path/to/cake/console/cake -app /path/to/app/dir schema run create DbAcl Con los controladores listos para la entrada de datos y con las tablas Acl inicializadas estamos listos para empezar cierto?... no del todo, aún necesitamos un poco de trabajo en los modelos de users y groups. Llamémoslo, hacerlos automágicamente ligados a Acl. 11.2.4 Act as a Requester (Solicitante) Para que Auth y Acl funcionen apropiadamente necesitamos asociar a nuestros modelos users y groups con registros en las tablas Acl. Para hacer esto usaremos el AclBehavior. El AclBehavior permite la conexión automágica entre los modelos y las tablas Acl. Su uso requiere la implementación de parentNode() en tu modelo. En nuestro modelo User agregaremos lo siguiente: var $name = 'User'; var $belongsTo = array('Group'); var $actsAs = array('Acl' => array('requester')); function parentNode() { if (!$this->id && empty($this->data)) { return null; } $data = $this->data; if (empty($this->data)) { $data = $this->read(); } if (!$data['User']['group_id']) { return null; } else { return array('Group' => array('id' => $data['User']['group_id'])); } } Entonces en nuestro modelo Group agrega lo siguiente: var $actsAs = array('Acl' => array('requester')); function parentNode() { return null; } Lo que hace esto es unir los modelos Group y User al Acl, y decirle a CakePHP que cada vez que crees un User o Group agregues una entrada en la tabla aros. Esto hace la administración de Acl muy fácil ya que tus AROs se ligan transparentemente a tus tablas de users y groups. De esta forma, cada vez que se crea o elimina un usuario la tabla de Aro se actualiza. Nuestros controladores y modelos ahora están preparados para agregar algunos datos iniciales, y nuestros modelos Group y User estan unidos a la tabla Acl. Ahora agregaremos unos groups y users usando los formularios que creamos con bake. Yo creé en este caso los siguientes grupos: • administrators • managers • users Tambien creé un user en cada group de tal forma de tener un user por cada grupo de acceso diferente para probarlos luego. Escribe todo facil de recordar, sobre todos los passwords. Si haces un SELECT * FROM aros; desde la consola de mysql obtendrás algo como: +----+-----------+-------+-------------+-------+------+------+ | id | parent_id | model | foreign_key | alias | lft | | | | | | 1 | 2 | 3 | 4 | 5 | 6 | NULL | Group | NULL | Group | NULL | Group | 1 | User 2 | User 3 | User | | | 1 | NULL 2 | NULL 3 | NULL 1 | NULL 2 | NULL 3 | NULL | | | | | | | rght | 4 | 8 | 12 | 3 | 7 | 11 | +----+-----------+-------+-------------+-------+------+------+ 1 | 5 | 9 | 2 | 6 | 10 | +----+-----------+-------+-------------+-------+------+------+ 6 rows in set (0.00 sec) Esto nos muestra que tenemos tres groups y 3 users. Los users estan anidados dentro de los grupos., lo que significa que podemos fijar permisos mediante grupos o por usuarios. Cuando modificamos un user, debes actualizar manualmente los ARo. Este codigo debería ser ejecutado cada vez que actualices la información del usuario: // Verificar si sus permisos de grupo han cambiado $oldgroupid = $this->User->field('group_id'); if ($oldgroupid !== $this->data['User']['group_id']) { $aro =& $this->Acl->Aro; $user = $aro->findByForeignKeyAndModel($this->data['User']['id'], 'User'); $group 'Group'); // Guardar en la tabla ARO $aro->id = $user['Aro']['id']; $aro->save(array('parent_id' => $group['Aro']['id'])); } = $aro->findByForeignKeyAndModel($this->data['User']['group_id'], 11.2.4.1 11.2.4.1 Group-only ACL In case we want simplified per-group only permissions, we need to implement bindNode() in User model. function bindNode($user) { return array('Group' => array('id' => $user['User']['group_id'])); } This method will tell ACL to skip checking User Aro's and to check only Group Aro's. Every user has to have assigned group_id for this to work. In this case our aros table will look like this: +----+-----------+-------+-------------+-------+------+------+ | id | parent_id | model | foreign_key | alias | lft | | | 1 | 2 | 3 | NULL | Group | NULL | Group | NULL | Group | 1 | NULL 2 | NULL 3 | NULL | | | | rght | 2 | 4 | 6 | +----+-----------+-------+-------------+-------+------+------+ 1 | 3 | 5 | +----+-----------+-------+-------------+-------+------+------+ 3 rows in set (0.00 sec) Nuestros AROs son automaticamente creados cuando nuevos usuarios y grupos son creados. el cual se llama 'controllers'.2. además. En AppController agrega el siguiente código a beforeFilter: $this->Auth->actionPath = 'controllers/'.5 Creando ACOs Ahora que ya tenemos nuestros users y groups (aros). 'alias' => 'controllers')). $this->Acl->Aco->save(). Las clases del núcleo ofrecen unas pocas formas de crear ACOs manualmente. Desafortunadamente no hay una manera mágica de hacer esto. de tal forma que cuando hacemos que ACL lo verifique. habilitar el login y logout. Desde la consola esto se ve así: cake acl create aco root controllers Mientras que desde el AclComponent se ve asi: $this->Acl->Aco->create(array('parent_id' => null. . Así como usaremos una raíz global para los ACO necesitaremos hacer una pequeña modificación a la configuración de nuestro AuthComponent. Ambos ejemplos crear nuestra 'raiz' (root) o ACO de nivel superior. El propósito de este nodo raiz es hacer fácil permitir/denegar (allow/deny) acceso en el ámbito global de la aplicación y permitir el uso del Acl para propósitos no relacionados con los controladores/acciones tales como verificar permisos de registro del modelo. pueda usar la ruta correcta al nodo cuando busque un(a) controlador/accion. AuthComponent necesita saber de la existencia de este nodo raíz.11. Se pueden crear ACOs a través de la consola o usando el AclComponent. podemos empezar a ingresar nuestros controladores en el Acl y fijar los permisos para cada grupo de usuarios. ¿Que hay de autogenerar los ACOs desde nuestros controladores y sus acciones?. no hay una forma pre-construida para insertar y conectar nuestros controladores y sus funciones dentro del Acl. } else { $root = $root[0]. ni propio del Controller. He escrito una función automática para construir mi tabla de Aco's. } $baseMethods = get_class_methods('Controller'). Puedes agregar y ejecutar esto en tu AppController o cualquier otro controlador con ese propósito. 'alias' => 'controllers')).2. $log[] = 'Creado el nodo Aco para los controladores'. $appIndex = array_search('App'. $root['Aco']['id'] = $aco->id.6 Una herramienta automática para crear ACOs Tal como se mencionó anteriormente. Esta función mirará en cada controlador de la aplicación. Sin embargo. $Controllers). $Controllers = Configure::listObjects('controller'). Añadirá cualquier metodo no privativo. $aco =& $this->Acl->Aco. * * @return void */ function buildAcl() { $log = array().11. /** * Reconstruye el Acl basado en los controladores actuales de la aplicación. // miramos en cada controlador en app/controllers . solo asegurate de eliminarlo antes de poner la aplicación en producción. $root = $aco->node('controllers'). } App::import('Core'. 'File'). if (!$root) { $aco->create(array('parent_id' => null. todos odiamos hacer tareas repetitivas como tipear lo que tal vez sean cientos de acciones en una aplicación grande. 'model' => null. $baseMethods[] = 'buildAcl'. $root = $aco->save(). if ($appIndex !== false ) { unset($Controllers[$appIndex]). '_'. } $methodNode = $aco->node('controllers/'. 'model' => null. continue. //buscar / crear nodo de controlador $controllerNode = $aco->node('controllers/'. } .$ctrlName. } } } debug($log). $methodNode = $aco->save(). 'model' => null.foreach ($Controllers as $ctrlName) { App::import('Controller'. 'alias' => $method)).$ctrlName).$ctrlName. para eliminar aquellos en el controlador //y en las acciones privadas foreach ($methods as $k => $method) { if (strpos($method. } if (in_array($method.'/'. $methods = get_class_methods($ctrlclass). $baseMethods)) { unset($methods[$k]). $controllerNode['Aco']['id'] = $aco->id. if (!$methodNode) { $aco->create(array('parent_id' => $controllerNode['Aco'] ['id']. continue. $ctrlName). } else { $controllerNode = $controllerNode[0]. 'Controller'.$method). $log[] = 'Creado el nodo Aco del controlador '. 'alias' => $ctrlName)). $method. if (!$controllerNode) { $aco->create(array('parent_id' => $root['Aco']['id']. $log[] = 'Creado el nodo Aco para '. $ctrlclass = $ctrlName . $controllerNode = $aco->save(). } //Limpieza de los metodos. 0) === 0) { unset($methods[$k]). Sin embargo. . * */ function _get_plugin_controller_names(){ App::import('Core'. Entonces lo que necesitamos es una función que nos entregará una lista de los nombres de los controladores de los plugins. // Obtener la lista de los archivos que terminan con // controller.'plugins'). que ya está funcionando.PluginControllerNombre.php $files = $folder->findRecursive('. quizás notes que tienes problemas accesando cualquier plugin que quizás estabas ocupando.*_controller\. de modo de obtener los nombres de los metodos al hacer un * App:import para cada uno de los plugins. $folder =& new Folder(). // Cambiamos al directorio de plugins $folder->cd(APP. Ahora que todo lo más complejo está hecho. Ahora. esto implica tener que limpiar a mano la tabla ACO. * * @return arreglo con los nombres de los plugins. es que necesita un App::import que siga la convencion de nombre de plugins. que es PluginNombre. necesitamos ahora configurar los permisos. no remueve los nodos de las acciones que ya no existen. 'Folder'). El truco para automatizar el controlador de ACO's para los plugins. 'File'. $paths = Configure::getInstance(). y eliminar el código que deshabilitó el AuthComponent anteriormente. La función de abajo hará exactamente eso: /** * Obtener los nombres de los controladores de plugins * * Esta funcion entrega un arreglo con los nombres de los controladores * de los plugins y además se asegura que los controladores están disponibles * para nosotros. // Obtener la lista de plugins $Plugins = Configure::listObjects('plugin'). y que los importe en la misma forma que lo hicimos arriba para los controladores normales.Quizás quieras mantener esta función cerca cuando añadas nuevos ACO's para todos los controladores & acciones que tiene tu aplicación cada vez que la ejecutes.php'). strlen($file)strlen('_controller. // Ciclo a través de los plugins foreach($Plugins as $pluginName){ if (preg_match('/^'. 0. $pluginName.'. } else { if (!App::import('Controller'.'.$file)) { debug('Error importando el archivo '.$pluginName. $file)){ // Primero nos deshacemos del AppController del plugin // Hacemos esto porque nunca lo llamamos directamente if (preg_match('/^'. $file. } . } // Ahora le agregamos el nombre del plugin al inicio // Esto lo necesitamos para poder obtener los nombres // de los métodos $files[$f] = $file.' para el plugin '. $file)){ unset($files[$f]).php'))).'App/'. // Obtener el nombre del controlador $file = Inflector::camelize(substr($file. } break.// Ciclo a través de los controladores que encontramos en el // directorio de plugins foreach($files as $f => $fileName) { // Obtener el nombre de archivo base $file = basename($fileName).'/'.$pluginName. } } } return $files.$pluginName). $Plugins). Ahora añadiremos algunas sentencias para permitir y denegar el acceso. en este caso no proveeremos una forma automática. function initDB() { $group =& $this->User->Group. $this->Acl->allow($group. usamos: cake acl grant $aroAlias $acoAlias [create|read|update|delete|'*']} * Necesita estar entre comillas simples ('*') Para permitir acceso a través del AclComponent haz lo siguiente: $this->Acl->allow($aroAlias. 'controllers/Posts'). $this->Acl->allow($group.2. //permite a los usuarios añadir y editar posts y widgets . //permite a los editores postear y accesar los widgets $group->id = 2. $this->Acl->allow($group. Una vez que hayas confirmado los permisos. Agrega lo siguiente a tu función temporal en tu UsersController e ingresa a la dirección adecuada en tu navegador para ejecutarla.Tu puedes modificar el código original para incluir los controladores de plugins al mezclarlos con la lista que obtuviste (ubicarlo antes del ciclo foreach): $Plugins = $this->_get_plugin_controller_names(). $this->Acl->deny($group. 'controllers'). //Permite a los administradores hacer todo $group->id = 1. elimina la función. Si haces un SELECT * FROM aros_acos deberías ver toda un montón de unos y ceros. 'controllers').7 Configurando los permisos Crear los permisos al igual que crear los ACO's no tiene una solucion mágica. Para permitir a los ARO's acceder a los ACO's desde la consola. 11. $acoAlias). 'controllers/Widgets'). $Controllers = array_merge($Controllers. Haremos que los metodos index y view sean de acceso público en el controlador PostsController y en el WidgetsController. En AppController::beforeFilter() agrega lo siguiente: $this->Auth->allowedActions = array('display'). 'controllers/Widgets/edit'). por lo que debemos usar una referencia a un objeto ARO que necesitemos. $this->Acl->allow($group. } Esto elimina el "interruptor de apagado" que colocamos anteriormente el los controladores de groups y users.$group->id = 3. 'controllers'). 'controllers/Widgets/add'). 'view'). Hemos permitido a los administradores accesar a todas las funciones. Debemos obtener una referencia del modelo Group y modificar su id para poder especificar el ARO que queremos. Ahora queremos eliminar las referencias al Auth->allowedActions en los controladores de users y groups. $this->Auth->allowedActions = array('index'. 'controllers/Posts/add'). 'controllers/Posts/edit'). $this->Acl->deny($group. $this->Acl->allow($group. El AclBehavior no configura el campo alias en la tabla aros. Los editores pueden accesar a todas las funcionalidades de los posts y los widgets. Sin embargo. y otorga acceso público a las acciones index y view en aquellos controladores. haciendolas públicas. } Ahora hemos seteado algunas reglas básicas de acceso. esto es debido a la forma en que trabaja el AclBehavior. . $this->Acl->allow($group. $this->Acl->allow($group. Entonces agregamos el siguiente código en los controladores de posts y widgets: function beforeFilter() { parent::beforeFilter(). Esto permite a los usuarios no autorizados para ver estas paginas. Quizás hayas notado que deliberadamente deje fuera index y view fuera de mis permisos ACL. en cualquier momento puedes eliminar estas acciones desde AuthComponent::allowedActions y los permisos de estas dos funciones volverán a su configuración original del Acl. En cambio los usuarios solo pueden accesar aquellas funciones que permiten agregar y editar posts y widgets. y cualquier intento de ver alguna página sin acceso público será redirigida a la página de login. necesitamos crear una vista de login antes de que alguien pueda loguerase. Cuando el acceso sea denegado el mensaje apropiado del Auth será desplegado si es que has agragado $session>flash('auth') 11. Ahora deberias poder loguerte y todo deberá funcionar automágicamente.Esto hace que la acción 'display' sea pública. Hace un momento dejamos esta función en blanco. echo $form->input('User.9 Logout (deslogueo) Ahora al logout. array('url' => array('controller' => 'users'. <h2>Login</h2> <?php echo $form->create('User'. Copia el layout por defecto que se encuentra en cake/libs/views/layouts/default. Esto mantendrá publica nuestra acción PagesController::display(). echo $form->input('User. .password').ctp agrega: $session->flash('auth').ctp . $this->redirect($this->Auth->logout()). ahora es el momento de llenarla.ctp si es que aún no lo has hecho. 'action' =>'login'))). En app/views/layouts/default. Esto es importante pues a menudo ésta es la accion accesada por defecto del routing de la aplicación.2.a la carpeta de layouts de tu aplicación si es que aún no lo has hecho. ?> Tal vez quieras agregar un flash() para los mensajes del componente Auth en tu layout.2. Agrega el siguiente código a app/views/users/login. echo $form->end('Login').username'). En UsersController::logout() añade lo siguiente: $this->Session->setFlash('Adios y nos vemos. Sin embargo. 11.').8 Logueo de Usuarios Nuestra aplicación ya esta bajo control de acceso. tienes a tu disposición un código reusable para facilmente expandir tu tabla de ACO a medida que tu aplicación crece. También puedes configurar los permisos a nivel global y también por controladores y acciones.10 Todo hecho En este momento ya deberías tener una aplicación controlada por los componentes Auth y Acl.2. Este método básicamente elimina la llave de la sesión del usuario y devuelve la url que puede ser usada en el redireccionamiento. pero pueden ser configurados a nivel de usuarios en cualquier momento.Esto establece el mensaje flash y saca al usuario de la aplicación usando el método logout del componente Auth. Los permisos de usuarios se hicieron al nivel de grupo. . 11. Además. Si es que hay otros datos en la sesión que necesiten ser eliminados se debe agregar ese código acá. cipherSeed que debe ser cambiada en app/config/core.1 Migrando desde CakePHP 1. Reemplazos de archivos de aplicación (importante) • webroot/index. • config/core. debe definirla previamente en app/config/bootstrap.2 al 1.3 Esta guía resume muchos de los cambios necesarios al migrar desde CakePHP 1. # Removed Constants Las siguientes constantes han sido eliminadas de CakePHP.php • CIPHER_SEED • PEAR • INFLECTIONS • VALID_NOT_EMPTY • VALID_EMAIL • VALID_NUMBER • VALID_YEAR # Configuration and application bootstrapping Bootstrapping Additional Paths.12 Apendices Notas adicionales sobre el desarrollo con CakePHP 12.2 a 1.php you may have variables like $pluginPaths or $controllerPaths. Cada sección contiene información relevante acerca de las modificaciones hechas a los métodos existentes así como los métodos que han sido eliminados/renombrados.3. Si su aplicación depende de una de ellas. • webroot/test.php: Reemplace este archivo si desea correr pruebas unitarias (unit tests).3. Reemplazada por la variable de la clase Configure Security.php . In your app/config/bootstrap.php: Han sido añadidas configuraciones adicionales requeridas por PHP 5.php: Debe ser reemplazado debido a cambios en el proceso de bootstrapping. => array('/full/path/to/controllers/'. App::build(array( 'plugins' => array('/full/path/to/plugins/'. 'helpers' => array('/full/path/to/helpers/'. '/next/full/path/to/vendors/'). y las características relacionadas fueron reprogramadas en un método para incrementar su flexibilidad. '/next/full/path/to/plugins/'). '/^(inflec|contribu)tors$/i' => . 'datasources' '/next/full/path/to/datasources/'). 'controllers' '/next/full/path/to/controllers/'). As of 1. array( 'rules' '\1ta'). => array('/full/path/to/components/'. 'shells' => array('/full/path/to/shells/'. You must use App::build() to modify paths. Inflector::rules('singular'. In the past app/config/core.php is loaded and the core cache configs are created before bootstrap. '/next/full/path/to/models/'). 'components' '/next/full/path/to/components/').3 RC1 the $pluginPaths variables will no longer work. Loading custom inflections inflections. => array('/full/path/to/datasources/'. 'models' => array('/full/path/to/models/'. '/next/full/path/to/helpers/'). 'behaviors' '/next/full/path/to/behaviors/'). Also changed is the order in which bootstrapping occurs. => array('/full/path/to/behaviors/'. 'irregular' => array('spins' => 'spinor') )).php is loaded. '/next/full/path/to/views/'). '/next/full/path/to/shells/'). => array('/^(bil)er$/i' => '\1'. In 1.php was loaded after app/config/bootstrap.3 core.php. 'locales' => array('/full/path/to/locale/'. '/next/full/path/to/locale/') )). 'vendors' => array('/full/path/to/vendors/'. se trataba de un archivo innecesario. 'uninflected' => array('singulars').There is a new way to add those paths. 'views' => array('/full/path/to/views/'. Ahora utilice Inflector::rules() para cargar las inflexiones personalizadas. This caused any App::import() in an application bootstrap to be un-cached and considerably slower than a cached include.php ha sido removido. php ⇒ model_behavior.php • App::import('Core'. 'Socket') ⇒ App::import('Core'. you will need to not use those methods.php ⇒ cake_schema.php and libs/model/behavior. 'CakeSession') • socket.php ⇒ cake_socket.php • App::import('Core'. libs/model/schema. the above renaming will not affect userland code.php • App::import('Model'.php • App::import('Core'.php ⇒ cake_session. 'CakeSchema') • behavior. . 'CakeSocket') • schema. with the added rules taking precedence over the core rules. # File renames and internal changes Library Renames Core libraries of libs/session.php. 'Behavior') ⇒ App::import('Core'. libs/socket.Will merge the supplied rules into the infection sets. 'Schema') ⇒ App::import('Model'.php have been renamed so that there is a better mapping between filenames and main classes contained within (as well as dealing with some name-spacing issues): • session. 'Session') ⇒ App::import('Core'. Inheritance from Object The following classes no longer extend Object: • Router • Set • Inflector • Cache • CacheEngine If you were using Object methods from these classes.php. 'ModelBehavior') In most cases. • Controller::set('title'. In 1. CookieComponent • del is deprecated use delete AclComponent + DbAcl Node reference checks done with paths are now less greedy and will no longer consume intermediary nodes when doing searches. Component • Component::triggerCallback has been added. But if you want to customize it. It is a generic hook into the component callback process.1. • Controller::$pageTitle has been removed. $var). Variables always appear in the view exactly as you set them. if you were expecting to get the last node you would need to use the path ROOT/Users/Users . $var) no longer sets $title_for_layout when rendering the layout. Use $this- >set('title_for_layout'. use $this->set('title_for_layout'. These methods are responsible for handling the controller startup and shutdown processes.3. • Controller has two new methods startupProcess and shutdownProcess. It supplants Component::startup(). Component::shutdown() and Component::beforeRender() as the preferred way to trigger callbacks. $var). $title_for_layout is still populated by default. In the past given the structure: ROOT/ Users/ Users/ edit The path ROOT/Users would match the last Users node instead of the first.1 Controller & Components Controller • Controller::set() no longer changes variables from $var_name to $varName.12. instead. You should update AppController::$components and AppController::$helpers to include these classes to retain existing behavior. var $components = array('Session'. Move the required layout files into app/views/elements 2. These change were done to make CakePHP more explicit and declarative in what classes you the application developer want to use.). 1. var $helpers = array('Session'. in your layout SessionComponent and SessionHelper are not automatically loaded. 'Form' .RequestHandlerComponent • getReferrer is deprecated use getReferer SessionComponent & SessionHelper • del is deprecated use delete SessionComponent::setFlash() second param used to be used for setting the layout and accordingly rendered a layout file. Which is something we want you to be able to avoid. ... This has been modifed to use an element. # Library Classes CakeSession • del is deprecated use delete . 'Auth'. In addition Session classes were the only magical component and helper. Both SessionComponent and SessionHelper are no longer automatically included without you asking for them. SessionHelper and SessionComponent now act like every other component and must be declared like any other helper/component. 'Html'..). Make sure you have echo $session->flash().. Rename the $content_for_layout variable to $message 3. In the past there was no way to avoid loading the Session classes without modifying core files. If you specified custom session flash layouts in your applications you will need to make the following changes. This change helps unify and normalize behavior amongst all classes. //to: Configure::write('Routing.3 do not require additional routes to be declared manually. array(.and _ or /[A-Z0-9-_+]+/. .)). Instead you should use Routing. It provided an inconsistent behavior with other prefix style routes in that it was treated differently.admin'..prefixes.admin is deprecated. Prefix routes in 1.. All prefix routes will be generated automatically. Router Routing. Be sure to move any flash layouts from app/views/layouts to app/views/elements and change instances of $content_for_layout to $message. Folder • mkdir is deprecated use create • mv is deprecated use move • ls is deprecated use read • cp is deprecated use copy • rm is deprecated use delete Set • isEqual is deprecated. See the New features guide for more information on using prefix routes.prefixes'.SessionComponent • SessionComponent::setFlash() now uses an element instead of a layout as its second parameter. 'admin'). Routed params should now only consist of alphanumeric chars. //Acceptable . array(. A small change has also been done to routing params. call String methods statically. String • getInstance is deprecated.. //from: Configure::write('Routing.php. Router::connect('/:$%@#param/:action/*'. Use == or ===. To update simply change your core.)). // BAD Router::connect('/:can/:anybody/:see/:m-3/*'. array('admin')).. and complicated route compiling. .3 the internals of the Router were heavily refactored to increase performance and reduce code clutter. 'action' => 'show')). If you need routes like this. You can no longer create routes like Router::connect('/([0-9]+)-p-(. array('event' => '[a-z0-9_-]+') ). array('controller' => 'products'. // old format: $url = array('controller' => 'posts'.For 1.*)/'. It was previously possible to use a greedy star in the middle of a route. 'action' => 'display').2 Also. 'action' => 'view'. $html->link($url). array('controller' => 'pages'. $this->redirect($url). Outside of these two edge-case features and the above changes the router behaves exactly as it did in 1. you will need to rewrite all your $html->link() and $this->redirect() calls to reflect this change. The side effect of this is two seldom used features were removed. If you previously used this approach for passing the ID parameter to actions. 'id' => $id). Next mid-route greedy star support has been removed. $id). it is recommended that you use route parameters with capture patterns. 'action' => 'view'. as they were problematic and buggy even with the existing code base. First path segments using full regular expressions was removed. // 1. // use cases: Router::url($url).2 result: /posts/view/123 // 1. people using the 'id' key in array-form URLs will notice that Router::url() now treats this as a named parameter. These routes complicated route compilation and impossible to reverse route. Router::connect( '/pages/*/:event'.3 result: /posts/view/id:123 // correct format: $url = array('controller' => 'posts'. This is no longer supported as mid-route greedy stars behaved erratically. See New Logging features for more information on changes made to logging. • Configure::write('modelPaths'. Use Debugger::output("txt").) replaced by App::build(array('models' => array.. returning possibly dangerous content. CakeLog::write is now called statically. • Sanitize::clean() now has a remove_html option.)) • Configure::read('modelPaths') replaced by App::path('models') • There is no longer a debug = 3. Configure and App • Configure::listObjects() replaced by App::objects() • Configure::corePaths() replaced by App::core() • Configure::buildPaths() replaced by App::build() • Configure no longer manages paths. The $cakeDebug variable has also been removed from View::renderLayout You should remove this variable reference to avoid errors. Object • Object::$_log has been removed. and untested.Dispatcher Dispatcher is no longer capable of setting a controller's layout/viewPath with request parameters. Sanitize • Sanitize::html() now actually always returns escaped strings.. array. This feature was also undocumented. The controller dumps generated by this setting often caused memory consumption issues making it an impractical and unusable setting. Control of these properties should be handled by the Controller... and must be used in conjunction with the encode parameter. In the past using the $remove parameter would skip entity encoding. has been renamed to . This will trigger the strip_tags feature of Sanitize::html(). not the Dispatcher. Debugger • Debugger::checkSessionKey() Debugger::checkSecurityKeys() • Calling Debugger::output("text") no longer works. findCount.php for the same reasons detailed above. • DboSource::query() now throws warnings for un-handled model methods. Also checkout the New features guide for more information on the additional methods added to Cache. Cache underwent some refactoring for CakePHP1. removed. in the name should be updated to use _ Cache In addition to being able to load CacheEngines from app/libs or plugins. Use Configure::load('plugin.3. not cache engine names. It should be noted that using an app/libs or plugin cache engine for the default cache config can cause performance issues as the import that loads these classes will always be uncached. The end result was a significant performance improvement with only a few minor API changes which are detailed below. tableprefix should be with the $tablePrefix property. which is now the canonical delete method. or manually include the cache engine class before configuring it.• Configure::load() can now load configuration files from plugins. findNeighbours. and instead an engine instance is made for each unique key created with Cache::config(). In addition Cache::isInitialized() now checks cache configuration names. The changes in Cache removed the singletons used for each Engine type. instead of . and any other custom construction behavior should be done in an overridden Model::__construct(). to load configuration files from plugins. It is recommended that you either use one of the core cache engines for your default configuration. You can still use Cache::set() or Cache::engine() to modify cache configurations. Cache::engine() was not needed and was removed.file'). Any configuration files in your application that use .1. 12. • Model::findAll. These refactorings focused around reducing the number and frequency of method calls.2 Model Databases and Datasources Model • Model::del() and Model::remove() have been removed in favor of Model::delete(). • Dynamic calling of setTablePrefix() has been removed. Since engines are not singletons anymore. Furthermore any non-core cache engine configurations should be done in app/config/bootstrap. This means. You can of course customize or override this element in your app by creating app/views/elements/sql_dump. .3 there is a new core element called sql_dump.ctp. If you have custom datasources that implement create(). This prevented custom datasources from using create() or update() correctly without some ugly hacks. which is passed to DataSource::read()). make sure to re-run your unit tests on 1. The sql_dump element will only generate output when Configure::read('debug') is equal to 2. Databases Most database configurations no longer support the 'connect' key (which has been deprecated since pre-1. • Missing behaviors will now trigger a cakeError. Instead. do the following: <?php echo $this->element('sql_dump'). and read() (since Model::exists() will make a call to Model::find('count').3. if you set var $useTable = false. Instead no conditions will be used • For Model::saveAll() the default value for option 'validate' is now 'first' instead of true Datasources • DataSource::exists() has been refactored to be more consistent with non-database backed datasources. update().. var $useDbConfig = 'custom'.trying to run them as queries.3. DboSource no longer automatically outputs SQL logs.2). set 'persistent' => true or false to determine whether or not a persistent database connection should be used SQL log dumping A commonly asked question is how can one disable or remove the SQL log dump at the bottom of the page?. people starting transactions improperly via the $this->Model->begin() syntax will need to update their code so that it accesses the model's DataSource object directly. ?> You can place this element anywhere in your layout or view. it was impossible for Model::exists() to return anything but false. For 1. Previously. • Missing validation methods will now trigger errors in development mode. If you want to output SQL logs in 1. In previous versions the HTML SQL log generation was buried inside DboSource. • Model::find(first) will no longer use the id property for default conditions if no conditions are supplied and id is not empty. ctp • View::set('title'. in your controllers. Use $this->set('title_for_layout'. used truncate() instead. use $this->set('title_for_layout'. If you previously overrode AppHelper::output() to force helpers to auto-echo you will need to update your view files to manually echo helper output. $var). • TextHelper::highlight() no longer has: • an $highlighter parameter. first() and last() . • The $cakeDebug layout variable associated with debug = 3 has been removed. Use $options['ending'] instead. $title_for_layout is still populated by default. • View::$pageTitle has been removed. • TextHelper::truncate() no longer has: • an $ending parameter. Also see the notes related to SQL log dumping and Configure for more information. All core helpers no longer use Helper::output(). Use $options['exact'] instead. or rename your views to use . instead. The method was inconsistently used and caused output issues with many of FormHelper's methods. • an $considerHtmlparameter. • Automagic support for . Use $options['format'] instead. Use $options['html'] instead. $var) no longer sets $title_for_layout when rendering the layout. TextHelper • TextHelper::trim() is deprecated.thtml view file extension has been removed either declare $this>ext = 'thtml'. Use View::element() instead. PaginatorHelper PaginatorHelper has had a number of enhancements applied to make styling easier. Use $options['html'] instead.# View and Helpers View • View::renderElement removed. next(). But if you want to customize it. prev(). Remove it from your layouts as it will cause errors. • an $exact parameter. • an $considerHtmlparameter. $var). The disabled state of these methods now defaults to <span> tags instead of <div> tags. longer longer has has a parameter. • Default urls generated by form helper no longer contain 'id' parameter. prev() adds a class of prev. sort() will add the direction currently being sorted. sort(). next() adds a class of next. • FormHelper::secure() and FormHelper::create() no longer create hidden fieldset elements. • FormHelper::submit() Can now create other types of inputs other than type=submit. parameter. $attributes['empty'] instead. Instead they create hidden div elements. longer longer has has a a parameter. If you want to generate those types of inputs use FormHelper::submit() with a 'type' => 'reset' option for example. • FormHelper::button() Now creates <button> elements instead of reset or clear inputs. either asc or desc. Also enables reverse routing to work in a more intuitive fashion with default FormHelper urls. Use Use $attributes['empty'] instead. Use the type option to control the type of input generated. • FormHelper::minute() • FormHelper::meridian() • FormHelper::select() no $attributes['empty'] instead. $attributes['empty'] instead. passedArgs are now auto merged with url options in paginator. parameter. prev(). Use $attributes['empty'] instead. next() now add additional class names to the generated html. Use Use $attributes['empty'] instead. Use Use $attributes['empty'] instead. This improves validation with . no longer longer has has parameter. FormHelper • FormHelper::dateTime() • FormHelper::year() • FormHelper::month() • FormHelper::day() no no no no no longer has a $showEmpty $showEmpty $showEmpty $showEmpty a $showEmpty a a $showEmpty $showEmpty parameter. This makes default urls more consistent with documented userland routes. parameter. Use $options['escape'] instead. parameter. to your session->flash() calls. but not different named parameters or query string parameters. Also be sure to check the Form helper improvements for additional changes and new features in the FormHelper. and allows cacheAction to work with all custom routing. HtmlHelper • HtmlHelper::meta() no longer has an $inline parameter. You must add an echo $session->flash(). If you need to have custom cache durations for specific argument sets you will need to detect and update cacheAction in your controller. Use Use $options['escape'] instead. It has been merged with the $options array. This makes configuring $cacheAction easier as its no longer coupled to the routing. CacheHelper CacheHelper's interactions with Controller::$cacheAction has changed slightly. parameter. and was changed to create consistency in helper methods. You also could set different cache durations for different passed argument values. $options['escape'] instead. this caused caching to break whenever routes were changed. flash() was the only helper method that auto outputted. SessionHelper • flash() no longer auto echos. In the past if you used an array for $cacheAction you were required to use the routed url as the keys. • HtmlHelper::link() • HtmlHelper::para() • HtmlHelper::div() • HtmlHelper::tag() • HtmlHelper::css() no longer has an $escapeTitle an an $escape $escape $escape $inline parameter. Both of these limitations/inconsistencies have been removed.HTML4. Use Use $options['escape'] instead. no no longer longer has has an an parameter. $options['inline'] instead. . You now use the controller's action names as the keys for $cacheAction. no no longer longer has has parameter. the format must be using the strftime() style. # Console and shells Shell Shell::getAdmin() has been moved up to ProjectTask::getAdmin() Schema shell • cake schema run create has been renamed to cake schema create • cake schema run update has been renamed to cake schema update Console Error Handling The shell dispatcher has been modified to exit with a 1 status code if the method called on the shell explicitly returns false. It will also take parameters as in 1. • TimeHelper::i18nFormat() has been added Deprecated Helpers Both the JavascriptHelper and the AjaxHelper are deprecated. Before the value returned from the method was used directly as the status code for exiting the shell.2. . Returning anything else results in a 0 status code. When called with this parameter order it will try to automatically convert the date format into the preferred one for the current locale. These are the changes made in the TimeHelper API: • TimeHelper::format() can now take a time string as first parameter and a format string as the second one.x version to be backwards compatible. You should replace • $javascript->link() with $html->script() • $javascript->codeBlock() with $html->scriptBlock() or $html>scriptStart() and $html->scriptEnd() depending on your usage.TimeHelper TimeHelper has been refactored to make it more i18n friendly. and the JsHelper + HtmlHelper should be used in their place. The new method TimeHelper::i18nFormat() has been added and will take localization data from a LC_TIME locale definition file in app/locale following the POSIX standard. but in this case format string must be compatible with date(). Internally almost all calls to date() have been replaced by strftime(). If your Group tests do not run. Before if arguments were available the method was returning true. Test Suite & schema vendors/css. . vendors/js. Test Suite and Unit Tests Group tests should now extend TestSuite instead of the deprecated GroupTest class.3 in favour of plugin and theme webroot directories. // outputs: Error: Invalid Foo Please provide bar. it will not in future versions. plugin and theme assets Vendor asset serving has been removed in 1. it now returns the shifted argument instead.1. Schema files used with the SchemaShell have been moved to app/config/schema instead of app/config/sql Although config/sql will continue to work in 1. Before if no arguments were available the method was returning false.Shell methods which are returning 1 to indicate an error should be updated to return false instead.3. it now returns null. They have been replaced with plugin and theme webroot directories. 'Please provide bar. It's signature is now similar to Shell::stdout(). Vendor.3 Vendors.'). and vendors/img Support for these three directories. // exits with status code 1 ShellDispatcher::stderr() has been modified to not prepend Error: to the message anymore. $this->error('Invalid Foo'. you will need to update the base class. Shell::error() has been modified to exit with status code 1 after printing the error message which now uses a slightly different formatting. both in app/vendors as well as plugin/vendors has been removed. it is recommend that the new path is used. 12. ShellDispatcher::shiftArgs() The method has been modified to return the shifted argument. EmailComponent • Ahora se puede obtener el contenido del correo electrónico creado. 'action' => 'login') ) ).3 introdujo varias características nuevas. # Componentes SecurityComponent Los métodos requireXX como requireGet y requirePost ahora aceptan un único array como argumento además de una colección de cadenas de texto. 'Auth' => array( 'userModel' => 'MyUser'. Configuración de componentes La configuración para todos los componentes del núcleo puede ser definida desde el array $components. var $components = array( 'Cookie' => array( 'name' => 'MyCookie' ). Esta guía intenta resumir los cambios y vincular a la documentación expandida donde sea necesario. $this->Security->requirePost(array('edit'.2 Nuevas características en CakePHP 1. De la misma forma que los comportamientos. Estas propiedades almacenan el contenido en html y en texto plano respectivamente. Esto debería reducir la cantidad de código en los métodos beforeFilter(). • Muchos de los métodos privados (private) del EmailComponent's ahora son protegidos (protected) para facilitar la extensión de los mismos.12. leyendo $this->Email>htmlMessage y $this->Email->textMessage. facilitando la configuración de . • EmailComponent::$to ahora también puede ser un array.3 CakePHP 1. se pueden declarar parametros de configuración cuando se declara el componente. 'update')). 'loginAction' => array('controller' => 'users'. # View & Helpers Helpers can now be addressed at $this->Helper->func() in addition to $helper>func().múltiples destinatarios. PaginatorHelper::next() and PaginatorHelper::prev() now generate span tags by default. Since this method is defined in Helper it is available to all subclasses. . This method allows you to set currency parameter sets.css' $stamped = $this->Html->assetTimestamp($path). instead of divs. y mejorando la consistencia con otras propiedades. It will add timestamps to any asset under WWW_ROOT. TextHelper highlight() now accepts an array of words to highlight.generic. It works with Configure::read('Asset.timestamp == force $path = 'css/cake. Assuming Asset. y permite controlar el encabezado Message-ID del correo electrónico. New JsHelper and new features in HtmlHelper See JsHelper documentation for more information Pagination Helper Pagination helper provides additional css classes for styling and you can set the default sort() direction. so you don't have to retype them. • EmailComponent::$messageId ha sido añadida. NumberHelper A new method addFormat() has been added. //$stamped contains 'css/cake. just as before. This allows view variables and helpers to share names and not create collisions.css?5632934892' The appended timestamp contains the last modification time of the file. Helper Helper::assetTimestamp() has been added.generic. but the functionality used in Html and Javascript helpers has been made available to all helpers.timestamp'). Once dropped cache engines are no longer readable or writeable. You can now provide custom Cache adapters in app/libs as well as in plugins using $plugin/libs. in Or either in app/libs/cache/my_custom_cache. • Cache::configured() returns an array of configured Cache engine keys. array('before' => 'Kr.php plugins need to use the plugin dot syntax. • Cache::increment() Perform an atomic increment on a numeric value. 12. Cache adapters must be in a cache directory. FormHelper The form helper has had a number of improvements and API modifications. .$this->Number->addFormat('NOK'. This is not implemented in FileEngine. # Caching Cache engines have been made more flexible in 1. $formatted = $this->Number->currency(1000..3 which make introspection and testing teardown easier. • Cache::drop($config) drops a configured Cache engine. See New Logging features for more information. see Form Helper improvements for more information. ')). If you had a cache engine named MyCustomCacheEngine it would as be an placed app/libs.php as part of a plugin. array( 'engine' => 'CachePack. • Cache::decrement() Perform an atomic decrement on a numeric value.2. This is not .MyCustomCacheEngine'. Cache configs from New Cache methods Cache has a few new methods for 1.3.. 'NOK'). App/plugin cache engines can also override the core engines. $plugin/libs/cache/my_custom_cache. Cache::config('custom'. both in features and flexibility. )).1 Logging Logging and CakeLog have been enhanced considerably. .php. read and write schema files to plugins. # Models. To import a Datasource from a plugin use App::import('Datasource'. CakeSchema also supports tableParameters. CakeSchema CakeSchema can now locate.implemented in FileEngine.. making debugging why validation isn't working easier. Using plugin datasources in your database. Each Dbo implements the tableParameters they support.. comments. 'MyPlugin. you could do: var $lastFm = array( 'datasource' => 'WebservicePack. and table engine type. see below for changes to SchemaShell. For example if you had a WebservicePack plugin with a LastFm datasource (plugin/webservice_pack/models/datasources/last_fm. • Models now support virtual fields Behaviors Using behaviors that do not exist. The SchemaShell also exposes this functionality.php You can use plugin datasources by setting the datasource key with the plugin name. now triggers a cakeError making missing behaviors easier to find and fix.php). charset. datasources & datasources from plugins Datasources can now be included loaded with App::import() and be included in plugins! To include a datasource in your plugin you put it in my_plugin/models/datasources/your_datasource. Model • Missing Validation methods now trigger errors. Table Parameters are non column specific table information such as collation.YourDatasource').LastFm' . Behaviors and Datasource App::import(). tableParameters in SQLite . tableParameters in Postgres . 'tableParameters' => array( 'engine' => 'InnoDB'.. 'default' => 0). 'indexes' => array( 'PRIMARY' => array('column' => 'id'.. 'comment' => array('type' => 'text'). • charset Control the character set used for tables. • encoding Control the encoding used for tables. 'post_id' => array('type' => 'integer'.. fieldParameters allow you to control MySQL specific settings per column. 'charset' => 'latin1'. In addition to tableParameters MySQL dbo's implement fieldParameters. Much like indexes: var $comments => array( 'id' => array('type' => 'integer'. • engine Control the storage engine used for your tables. • charset Set the character set used for a column • encoding Set the encoding used for a column See below for examples on how to use table and field parameters in your schema files.. 'collate' => 'latin1_general_ci') ). 'null' => false. 'post_id' => array('column' => 'post_id'). 'default' => 0.tableParameters in MySQL MySQL supports the greatest number of tableParameters. 'null' => false. . You can use tableParameters to set a variety of MySQL specific settings. 'key' => 'primary'). Using tableParameters in schema files You use tableParameters just as you would any other key in a schema file.. 'unique' => true)..). # Console Bake Bake has had a number of significant changes made to it. The Acl shell dataSource switch has been removed. If you use a schema file that contains options and features your database does not implement. err() and hr() now accept a $newlines parameter which is passed to nl() and allows for controlling how newlines are appended to the output. Output Shell::nl() has been added. For example if you imported the above schema to a PostgreSQL server. Use the Configure settings instead.. Shell::out(). all of the tableParameters would be ignore as PostgreSQL does not support any of the included options. User.1.foreign_key ie. . allowing a parameterless usage.is an example of a table using tableParameters to set some database specific settings. You no longer need to know or use the aco/aro id for commands.. those options will be ignored.. This is especially useful if you're often using $this->out('') for outputting just a single newline. It returns a single or multiple linefeed sequences. Acl Shell All AclShell commands now take node parameters. It expects and will create schema files in $plugin/config/schema . node parameters can be either an alias path like controllers/Posts/view or Model. Shell::out() and Shell::err() have been modified. SchemaShell The Schema shell can now read and write Schema files and SQL dumps to plugins. Those changes are detailed in the bake updates section Subclassing The ShellDispatcher has been modified to not require shells and tasks to have Shell as their immediate parent anymore. 'action' => 'index'. while having no defined asset filter will create 404 status code responses. if they do not an error will be triggered. Commonly a custom route class will override the parse() and/or match() methods found in CakeRoute to provide custom handling.3 is the ability to create and use your own Route classes. 'member')). Likewise. 'admin' => true)). 'action' => 'index'. 'admin' => false)). This class handles the parsing and reverse matching of an individual connected route. if you are in a prefixed url and want to go to a non-prefixed url. 'action' => 'index'.php you will be able to do the following from a non-prefixed url: $this->Html->link('Go'. array('admin'.2. Dispatcher • Accessing filtered asset paths. Inflector::rules('transliteration'. 'member' => true)). Developer route classes must extend CakeRoute. Also new in 1. array('controller' => 'posts'. 'member' => false)). They use the same syntax and persist/behave in the same way. array('/å/' => . array('controller' => 'posts'. # Library classes Inflector You can now globally customize the default transliteration map used in Inflector::slug using Inflector::rules. You can implement any special routing features that may be needed in application routing classes.# Router and Dispatcher Router Generating urls with new style prefixes works exactly the same as admin routing did in 1. in your core. 'action' => 'index'. do the following: $this->Html->link('Go'. $this->Html->link('Go'. array('controller' => 'posts'. eg. array('controller' => 'posts'.prefixes'.3 the router has been internally rebuilt. Assuming you have Configure::write('Routing. Route classes For 1. $this->Html->link('Go'. and a new class CakeRoute has been created. //imports app/libs/image_manipulation. Set::apply('/Movie/rating'. This directory can also be part of plugins. Would return the sum of all Movie ratings in $data.'aa'.Geocode'). This allows you to separate your organization's internal libraries from vendor libraries. App::import() has also been updated to import from libs directories. which allows you to apply callbacks to the results of Set::extract and do so in either a map or reduce fashion. Set Set has a new method Set::apply(). Libs directories are intended to contain 1st party libraries that do not come from 3rd parties or external vendors. File • File now has a copy() method. 'Geocoding. This can be used to determine/define the text direction of the locale being used. 'ImageManipulation').file').php . to a new location. //imports app/plugins/geocoding/libs/geocode. Any configuration files in your application that use .php You can also import libs files from plugins App::import('Lib'. L10N All languages in the catalog now have a direction key. 'array_sum'). $data. to load configuration files from plugins. It copies the file represented by the file instance. Configure • Configure::load() can now load configuration files from plugins. located at $plugin/libs. in the name should be updated to used _ App/libs In addition to app/vendors a new app/libs directory has been added. Use Configure::load('plugin. '/ø/' => 'oe')) The Inflector now also internally caches all data passed to it for inflection (except slug method). App::import('Lib'. In the past you would need to override __construct() and work around ErrorHandler's desire to convert all error methods into error404 when debug = 0.LC_TIME. In 1. For example. // return the preferred dates format for France You can read a complete guide of possible values in LC_TIME definition file in this page # Miscellaneous Error Handling Subclasses of ErrorHandler can more easily implement additional error methods.3 is medium instead of high • There is a new configuration value Security.level in 1. just put the file in there).LC_TIME. i18n Now you can use locale definition files for the LC_TIME category to retrieve date and time preferences for a specific language. Configuration • The default Security.language'. and a warning will be generated in development mode when the value matches its default value. Just use any POSIX compliant locale definition file and store it at app/locale/language/ (do not create a folder for the category LC_TIME.cipherSeed this value should be customized to ensure more secure encrypted cookies. Copy the part corresponding to LC_TIME into app/locale/fr_fr/LC_TIME file.prefixes scaffolding has been updated to allow the . // returns an array with the month names in French $dateFormat = __c('d_fmt'.The remainder of lib importing syntax is identical to vendor files.'fr-fr'). You can then access the time preferences for French language this way: Configure::write('Config. you know how to import libs files with unique names.true).true). Scaffolding With the addition of Routing. then you will need to do it manually. So if you know how to import vendor files with unique names. if you have access to a machine running debian or ubuntu you can find a french locale file at: /usr/share/i18n/locales/fr_FR.3. // set the current language $monthNames = __c('mon'. error methods that are declared in subclasses are not converted to error404. If you want your error methods converted into error404. . and still not afford the flexibility needed for all cases. array('admin'. } function postal($check) { . but must be imported before attempting to use it. Instead of trying to add every locale to Validation itself. Validation After 1. null. In 1. } } This file could be placed anywhere in your application. 'member')). there were numerous requests to add additional localizations to the phone() and postal() methods. var $validate = array( 'phone_no' => array('rule' => array('phone'. This approach allows you to create classes that handle a subset or group of locales. phone() and postal() will pass off any country prefix it does not know how to handle to another class with the appropriate name.3. The usage of the individual .scaffolding of any one prefix. which would result in large bloated ugly methods..2 was released. } Would use scaffolding for member prefixed urls. Validation will see that it cannot handle the 'nl' locale and will attempt to delegate out to NlValidation::postal() and the return of that method will be used as the pass/fail for the validation. an alternate path was taken. null. Configure::write('Routing... 'nl')) ). For example if you lived in the Netherlands you would create a class like class NlValidation { function phone($check) { . 'nl')).prefixes'. something that a large switch would not have. 'postal_code' => array('rule' => array('postal'. In your model validation you could use your NlValidation class by doing the following. class PostsController extends AppController { var $scaffold = 'member'. When your model data is validated. // Validates IPv4 Addresses only Validation::ip($someAddress. It will check that a given string matches a uuid by pattern only. 'IPv4'). IP Address Validation Validation of IP Addresses has been extended to allow strict validation of a specific IP Version. the ability to pass off to another validator has been added. Validation::ip($someAddress). . It will also make use of PHP native validation mechanisms if available.validation methods has not changed. // Validates both IPv4 and IPv6 Validation::ip($someAddress. It does not ensure uniqueness of the given uuid. 'IPv6'). // Validates IPv6 Addresses only Validation::uuid() A uuid() pattern validation has been added to the Validation class.