3.7.4 Salvando Tus Datos
CakePHP hace que el salvado de los datos del modelo sea instantáneo. 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'
)
)
La mayoría de las veces no necesitarás preocuparte por este formato: los ayudantes de CakePHP HtmlHelper, FormHelper, y métodos de búsqueda empaquetan los datos en este formato. Si estás usando alguno de los ayudantes, los datos también están convenientemente disponibles en $this->data para su uso rápido.
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...
if($this->Receta->save($this->data)) {
// Establede un mensaje flash y redirige.
$this->Session->setFlash("Receta guardada!");
$this->redirect('/recetas');
}
}
// Si no hay datos de formularo, busca la receta a editar y pásala a la vista
$this->set('receta', $this->Receta->findById($id));
}
function edit($id) {// Ha POSTeado algún dormulario datos?if(!empty($this->data)) {// Si el formulario puede ser validado y salvado...if($this->Receta->save($this->data)) {// Establede un mensaje flash y redirige.$this->Session->setFlash("Receta guardada!");$this->redirect('/recetas');}}// Si no hay datos de formularo, busca la receta a editar y pásala a la vista$this->set('receta', $this->Receta->findById($id));}
Una nota adicional: cuando se llama a save(), 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). Si por alguna razón tus datos no se graban, comprueba si alguna regla de validación se está incumpliendo.
Hay unos pocos métodos relacionados con el salvado que encontrarás útiles:
save(array $datos = null, boolean $validar = true, array $listaCampos = array())
Mostrado arriba, este método graba datos formateados en array. El segundo parámetro ($validar) te permite eludir la validación, y el tercero ($listaCampos) te permite proveer una lista de campos del modelo a ser grabados. Como seguridad añadida, puedes limitar los campos grabados a aquellos listados en $listaCampos.
Una vez que un salvado ha sido completado, el identificador ID del objeto se encuentra en el atributo $id del objeto del modelo (algo especialmente útil cuando se crean nuevos objetos).
$this->Ingrediente->save($datosNuevos); $nuevoIngredienteId = $this->Ingrediente->id;
$this->Ingrediente->save($datosNuevos);$nuevoIngredienteId = $this->Ingrediente->id;
Cuando se llama a save() en un bucle, no olvides llamar a create().
create(array $datos = array())
Este método resetea el estado del modelo para grabar nueva información.
Si se pasa el parámetro $datos (usando el formato de array descrito arriba), la instancia del modelo estará lista para salvar con esos datos (accesibles en $this->data).
saveField(string $nombreCampo, string $valorCampo, $validar = false)
Usado para salvar un único valor de un campo. Establece el ID del modelo ($this->nombreModelo->id = $id) antes de llamar a saveField(). Cuando usas este método, $nombreCampo debería contener sólo el nombre del campo, no el nombre del modelo y campo.
Por ejemplo, para actualizar el título de una entrada de un blog, la llamada a saveField desde un controlador debería parecerse a esto:
$this->Entrada->saveField('titulo', 'Un Nuevo Título para un Nuevo Día');
$this->Entrada->saveField('titulo', 'Un Nuevo Título para un Nuevo Día');
updateAll(array $campos, array $condiciones)
Actualiza varios registros en una única llamada. Los registros a ser actalizados están identificados por el array $conditions, y los campos a ser actualizados, así como sus valores, están identificados por el array $fields.
Por ejemplo, para aprobar a todos los panaderos que han sido miembros durante más de un año, la llamada de actualización debería ser algo como:
$este_año = date('Y-m-d h:i:s', strtotime('-1 year'));
$this->Panadero->updateAll(
array('Panadero.approved' => true),
array('Panadero.created <=' => "$este_año")
);
$este_año = date('Y-m-d h:i:s', strtotime('-1 year'));$this->Panadero->updateAll(array('Panadero.approved' => true),array('Panadero.created <=' => "$este_año"));
El array $campos acepta expresiones SQL. Los valores literales deberían ser entrecomillados manualmente.
Por ejemplo, para cerrar todos los tickets que pertenecen a cierto vendedor:
$this->Ticket->updateAll(
array('Ticket.estado' => "'cerrado'"),
array('Ticket.vendedor_id' => 453)
);
$this->Ticket->updateAll(array('Ticket.estado' => "'cerrado'"),array('Ticket.vendedor_id' => 453));
saveAll(array $datos = null, 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, $data necesita ser un array de registros indexado numéricamente como esto:
Array ( [0] => Array ( [titulo] => titulo 1 ) [1] => Array ( [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
(
[comentario] => Comment 2
[comentario] => 2
)
)
)
3.7.4.1 Guardando Datos de Modelos Relacionados (hasOne, hasMany, belongsTo)
Cuando estamos trabajando con modelos asociados, es importante tener en cuenta que al guardar los datos de un modelo hay que hacerlo con el correspondiente modelo de CakePHP. Si estás guardando una nueva Entrada y sus Comentarios asociados, entonces deberías usar ambos modelos, Entrada y Comentario, durante la operación de guardado.
Si ninguno de los registros de los modelos asociados existe aún (por ejemplo, quieres guardar registros de un nuevo Usuario y su Perfil relacionado a la vez ), primero necesitarás guardar el modelo primario o padre.
Para tener una idea de cómo funciona esto, imaginemos que tenemos una acción en nuestro controlador de usuarios UsersController que maneja el guardado de un nuevo usuario y su perfil correspondiente. 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.
<?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);
// El ID del nuevo Usuario está ahora en $this->User->id, así que lo
// añadimos a los datos a grabar y grabamos el Perfil
$this->data['Perfil']['usuario_id'] = $this->Usuario->id;
// Como nuestro "Usuario hasOne Perfil", podemos acceder
// al modelo Perfil a través del modelo Usuario
$this->Usuario->Perfil->save($this->data);
}
}
?>
<?phpfunction add() {if (!empty($this->data)) {// Podemos guardar los datos de Usuario// deberían estar en: $this->data['Usuario']$this->Usuario->save($this->data);// El ID del nuevo Usuario está ahora en $this->User->id, así que lo// añadimos a los datos a grabar y grabamos el Perfil$this->data['Perfil']['usuario_id'] = $this->Usuario->id;// Como nuestro "Usuario hasOne Perfil", podemos acceder// al modelo Perfil a través del modelo Usuario$this->Usuario->Perfil->save($this->data);}}?>
Como norma general, cuando trabajamos con asociaciones hasOne, hasMany y belongsTo ('tiene un', 'tiene varios', y 'pertenece a'), todo es cuestión de las claves. La idea básica es coger la clave de un modelo y ponerla en el campo de clave foránea en el otro. A veces esto puede implica usar el atributo $id de la clase del modelo después de save(), pero otras veces podría simplemente implicar obtener el ID desde un campo oculto de un formulario POSTeado a una acción del controlador.
Para complementar el enfoque básico usado arriba, CakePHP también ofrece el método muy útil saveAll, el cual te permite validar y grabar múltiples modelos de golpe. Además, saveAll provee de soporte transaccional para asegurar la integridad de los datos en tu base de datos (p.ej. si un modelo falla en la grabación, los otros modelos tampoco serán grabados).
Para que las transacciones funcionen correctametne en MySQL, tus tablas han de usar el mecanismo InnoDB. Recuerda que las tablas MyISAM no soportan transacciones.
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.
Primero, necesitas construir tu formulario tanto para el modelo Compañía como el modelo Cuenta (asumismo que Compañía hasMany Cuenta).
echo $form->create(Compañía, array('action'=>'añadir'));
echo $form->input('Compañía.nombre', array('label'=>'Nombre de compañía'));
echo $form->input('Compañía.descripción');
echo $form->input('Compañía.localización');
echo $form->input('Cuenta.0.nombre', array('label'=>'Nombre de cuenta'));
echo $form->input('Cuenta.0.nombreusuario');
echo $form->input('Cuenta.0.email');
echo $form->end('Añadir');
echo $form->create(Compañía, array('action'=>'añadir'));echo $form->input('Compañía.nombre', array('label'=>'Nombre de compañía'));echo $form->input('Compañía.descripción');echo $form->input('Compañía.localización');echo $form->input('Cuenta.0.nombre', array('label'=>'Nombre de cuenta'));echo $form->input('Cuenta.0.nombreusuario');echo $form->input('Cuenta.0.email');echo $form->end('Añadir');
Echemos un vistazo a la manera en que hemos nombrado los campos del formulario para el modelo Cuenta. Si Compañía es nuestro modelo principal, saveAll esperará que los datos de los modelos relacionados (en este caso, Cuenta) llegue en un formado específico, y teniendo Cuenta.0.nombreCampo es exactamente lo que necesitamos.
El nombrado de campos de arriba es necesario para la asociación hasMany. Si la asociación entre los modelos es hasOne, necesitarás usar la notación NombreModelo.nombreCampo para el modelo asociado.
Ahora, en nuestro compañias_controler.php podemos crear una acción add():
function add() {
if(!empty($this->data)) {
$this->Compañia->saveAll($this->data, array('validate'=>'first'));
}
}
function add() {if(!empty($this->data)) {$this->Compañia->saveAll($this->data, array('validate'=>'first'));}}
Esto es todo para ello. Ahora nuestros modelos Compañía y Cuenta serán validados y grabados al mismo tiempo. Una cosa rápida que comentar aquí es el uso de array('validate'=>'first'): esa opción asegurará que ambos modelos son validados.
3.7.4.1.1 counterCache - Cachea tu count()
Esta función te ayuda a cachear el contador de datos relacionados. En vez de contar los registros manualmente mediante find('count'), el modelo mismo monitoriza cualquier inserción/borrado al modelo '$hasMany' asociado, e incrementa/decrementa un campo entero dedicado dentro de la tabla del modelo padre.
El nombre del campo consiste en el nombre del modelo en singular seguido de un guión de subrayado (_) y la palabra "count".
Supongamos que tienes un modelo llamado "ImagenAlbum" y un modelo llamado "Imagen", entonces añadirías un campo INT a la tabla "imagenes_albumes" llamado "imagen_count". Si tus nombres son más complejos, aquí está otro ejemplo: Con "EntradaBlog" y "ComentarioEntradaBlog", el nombre del campo debería ser "comentario_entrada_blog_count" y necesita ser añadido a "entradas_blog".
Una vez que has añadido el campo contador, estará bien que actives esta funcionalidad añadiendo la clave "counterCache" al array de asociaciones $belongsTo y establecer el valor a true.
class AlbumImagen extends AppModel {
var $hasMany = array(
'Imagen'
);
}
class Imagen extends AppModel {
var $belongsTo = array(
'AlbumImagen' => array('counterCache' => true)
);
}
class AlbumImagen extends AppModel {var $hasMany = array('Imagen');}class Imagen extends AppModel {var $belongsTo = array('AlbumImagen' => array('counterCache' => true));}
Ahora, todas las veces que añadar una nueva "Imagen" a "AlbumImagen" el número en "imagen_count" se incremetnará (o decrementará si realizas un borrado).
3.7.4.2 Guardando Datos de Modelos Relacionados (HABTM)
Grabar modelos que están asociados por hasOne,belongsTo y hasMany es bastante simple: simplemente rellenas el campo de clave foránea con el ID del modelo asociado. Una vez que está hecho, simplemente llamas al método save() del modelo y todo queda enlazado correctamente.
Con HABTM (Has And Belongs To Many), necesitas establecer el ID del modelo asociado en tu array de datos. Construiremos un formulario que crea una nueva etiqueta y la asocia al vuelo con alguna 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');
echo $form->input('Receta.id', array('type'=>'hidden', 'value' => $receta_id));
echo $form->input('Etiqueta.nombre');
echo $form->end('Añadir etiqueta');
?>
<?phpecho $form->create('Etiqueta');echo $form->input('Receta.id', array('type'=>'hidden', 'value' => $receta_id));echo $form->input('Etiqueta.nombre');echo $form->end('Añadir etiqueta');?>
En este ejemplo, puedes ver el campo oculto Receta.id cuyo valor se establece al ID de la receta a la que queremos enlazar la etiqueta. 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
}
}
function add() {// Graba la asociaciónif ($this->Etiqueta->save($this->data)) {// Hacer algo si todo fue bien}}
Y de esa manera, nuestra nueva Etiqueta es creada y asociada con Receta, cuyo ID estaba en $this->data['Receta']['id'].
