Transacciones en CakePHP

273 views
Skip to first unread message

Jesús Ángel

unread,
Nov 14, 2009, 5:42:21 AM11/14/09
to cakep...@googlegroups.com
Hola

Estoy con las ACL. Cuando edito un usuario (AroRequester) y le cambio de
grupo, tengo que actualizar la tabla de AROS para cambiar su nodo padre
(es decir, para actualizar el grupo al que pertenece).

El caso es que ahora mismo, primero guardo los datos del usuario en la
tabla users y después, si el grupo al que pertenece el usuario ha
cambiado, modifico la tabla aros convenientemente.

El problema es que si la actualización de la tabla aros falla, el
usuario se queda en un grupo y las ACLs aplican los permisos de otro...

La solución creo que pasa por usar una transacción que englobe las dos
operaciones.

He estabo buscando información sobre el uso de transacciones en CakePHP:

http://monmonja.com/blog/2008/07/transaction-on-cakephp-12/

$this->Model->begin();
$returnQuery = $this->Model->query($query);
if($returnQuery !== false){
$this->Model->commit();
}else{
$this->Model->rollback();
}

Pero en el API no veo esos métodos...

También he encontrado un behaviour:

http://bakery.cakephp.org/articles/view/transaction-behavior

Pero me gustaría usar las funciones base de Cake, si es que existen.

Gracias

Jesus Angel

unread,
Nov 14, 2009, 6:42:42 AM11/14/09
to CakePHP en Español
Me respondo a mi mismo.

Aunque no existen los métodos begin, commit y rollback en la clase
Model, podemos utilizarlos, lo que ocurre en ese caso es que el método
no está "manejado" y en su lugar se lanza una consulta contra la base
de datos usando el nombre del método como SQL:

//cake/libs/model/model.php
function call__($method, $params) {
$result = $this->Behaviors->dispatchMethod($this, $method, $params);

if ($result !== array('unhandled')) {
return $result;
}
$db =& ConnectionManager::getDataSource($this->useDbConfig);
$return = $db->query($method, $params, $this);

if (!PHP5) {
$this->resetAssociations();
}
return $return;
}

Yo lo que he hecho al final es crearme mis propios métodos para
manejar las transacciones y así poder tener algo de control sobre el
proceso (p.ej. si falla el begin):

// AppModel.php
protected function _begin() {
if (empty($this->db)) {
$this->db =& ConnectionManager::getDataSource($this->useDbConfig);
}
return $this->db->begin($this);
}

protected function _commit() {
return $this->db->commit($this);
}

protected function _rollback() {
return $this->db->rollback($this);
}

Lo que me falta ahora es poder informar al controlador de que la
transacción ha fallado desde el método afterSave(), que es donde yo
hago el commit o el rollback. El problema es que el resultado devuelto
por afterSave() no se tiene en cuenta, sólo se considera lo que
devuelve save().

¿Se os ocurre algo para informar de que el salvado de los datos ha
fallado desde el método afterSave()?


On 14 nov, 11:42, Jesús Ángel del Pozo Domínguez

Nachopitt

unread,
Nov 14, 2009, 10:08:50 AM11/14/09
to cakep...@googlegroups.com
Una sugerencia, ¿no te convendría mejor sobreescribir el método save y ahí hacer el guardado, manejo de transacciones y devolver el resultado? Saludos. Por cierto, ¿en qué casos podría fallar la actualización de la tabla aros?
--
Ignacio Alberto Zamora Esqueda
Ingeniero de Software
Corporación Tectronic, S.A. De C.V.
Tels: (33) 3615-44-11 y 36-15-45-11
Fax: (33) 3615-46-11
sist...@tectronic.com.mx
www.tectronic.com.mx

Jesús Ángel

unread,
Nov 14, 2009, 12:32:14 PM11/14/09
to cakep...@googlegroups.com
Gracias por la idea. Normalmente prefiero no tocar nada de los métodos
"mágicos" de CakePHP que lo hacen casi todo :-)

Respecto a tu pregunta, imagina que, por ejemplo, ejecutas la consulta
que guarda los datos del usuario y justo después se cae la base de
datos, se corta la conexión de red, se llena el disco, etc. Te has
quedado con un usuario que no tiene registro asociado en la tabla aros,
o que tiene asociado un registro con datos sin actualizar...

Saludos

Nachopitt

unread,
Nov 14, 2009, 4:13:30 PM11/14/09
to cakep...@googlegroups.com
Bueno... eso puede pasar siempre, y ya que me contestas (esta era mi intención al hacerte la pregunta) quiero creer que al tener en preocupación este cuestionamiento de "integridad referencial", también te estás preocupando por las demás partes del sistema donde pueda ocurrir lo mismo (en donde una tabla padre se puede quedar sin su respectivo hijo, o al revés, o se perdió la actualización de llaves foráneas de las tablas hijas, etc.). O de plano es el único lugar donde existe la preocupación? En caso contrario, ¿estás usando saveAll o varios saves encapsulados como un método del modelo con transacciones?.
 
Yo por mi parte pienso que realizar un método en el modelo que se encargue de esto y dentro poner las transacciones es lo más fácil. Tienes la lógica dentro de un solo lugar y de ahi mismo puedes informar al controlador (con el valor de retorno) sobre lo que sucedió, si falló o no.

Jesús Ángel

unread,
Nov 15, 2009, 3:38:16 AM11/15/09
to cakep...@googlegroups.com
Hola

Sí claro, no sólo me he preocupado de las transacciones en este caso,
sólo que hasta ahora los casos en los que tenía que guardar datos en
varias tablas lo había hecho con saveAll().

Gracias

El sáb, 14-11-2009 a las 15:13 -0600, Nachopitt escribió:
> Bueno... eso puede pasar siempre, y ya que me contestas (esta era mi
> intención al hacerte la pregunta) quiero creer que al tener en
> preocupación este cuestionamiento de "integridad referencial", también
> te estás preocupando por las demás partes del sistema donde pueda
> ocurrir lo mismo (en donde una tabla padre se puede quedar sin su
> respectivo hijo, o al revés, o se perdió la actualización de llaves
> foráneas de las tablas hijas, etc.). O de plano es el único lugar
> donde existe la preocupación? En caso contrario, ¿estás usando saveAll
> o varios saves encapsulados como un método del modelo con
> transacciones?.
>
> Yo por mi parte pienso que realizar un método en el modelo que se
> encargue de esto y dentro poner las transacciones es lo más fácil.
> Tienes la lógica dentro de un solo lugar y de ahi mismo puedes
> informar al controlador (con el valor de retorno) sobre lo que
> sucedió, si falló o no.
>

>
Reply all
Reply to author
Forward
0 new messages