Consideremos que tenemos un modelo de datos en el que existen relaciones entre nuestras entidades, como siempre, un ejempo: dentro de nuestra aplicación tendremos noticias, perteneciendo cada noticia a un autor.
Ahora supongamos que generamos el panel de administración de esta aplicación mediante el admingenerator de symfony. Tal como está escrito el código del AdminGenerator nos encontraremos con que, cada vez que el usuario intente eliminar un autor al que se le haya asignado alguna noticia se producirá un error debido a una violación de la restricción de clave externa.
El usuario que utilice la aplicación no debe, ni tiene, porque saber esto, y es más que posible que intente borrar un autor que lo cumpla. Al ver la pantalla con un error 500 (a él no le aparecerá el error detallado que nosotros sí podemos ver en el entorno de desarrollo) nos llamará asustado y querrá una solución ya!
La mala solución
Una forma de proceder es mediante la famosísima solución conocida como muerto-el-perro-se-acabó-la-rabia. Si quito el botón de eliminar, el usuario no podrá borrar el registro, y si no puede borrar el registro no aparecerá el error. Fin del problema.
Bueno, no tan rápido. Qué ocurre si el usuario descubre que jugando con las URLs puede seguir borrando el registro. No pasa nada, basta con darle credenciales especiales a esa acción, por ejemplo en security.yml.
Bueno, vale, y si se ha equivocado al crear el autor y quiere borrarlo justo después de haberlo creado. En ese caso no habría problemas para eliminarlo ya que al no tener asociado ninguna noticia no surgiría tal error. Parece pues, que esta solución “hace aguas”.
La buena solución
Desde mi punto de vista la solución óptima pasa por permitirle borrar autores al usuario pero indicarle cuando no pueda. Para ello lo que hacemos es sobreescribir el método executeDelete de la acción. Quedaría algo como esto:
<?php // app/backend/modules/author/actions/actions.php public function executeDelete(sfWebRequest $request) { $request->checkCSRFProtection(); $this->dispatcher->notify(new sfEvent($this, 'admin.delete_object', array('object' => $this->getRoute()->getObject()))); try { $this->getRoute()->getObject()->delete(); $this->getUser()->setFlash('notice', 'The item was deleted successfully.'); }catch (Exception $e){ $this->getUser()->setFlash('error', 'The item can not be deleted.'); } $this->redirect('@author'); }
Creo que debo una explicación
Creo que siempre he tenido un don (si es que se le puede llamar así) para poner nombres “rimbonbantes” a las cosas. El título de este post comienza con “… and if you just try”. Creo que el chiste (no comment) se entiende sólo si sabemos que el método executeDelete de la acción en baseAuthorActions es:
// /cache/modules/author/actions/actions.class.php public function executeDelete(sfWebRequest $request) { $request->checkCSRFProtection(); $this->dispatcher->notify(new sfEvent($this, 'admin.delete_object', array('object' => $this->getRoute()->getObject()))); if ($this->getRoute()->getObject()->delete()) { $this->getUser()->setFlash('notice', 'The item was deleted successfully.'); } $this->redirect('@author'); }
Para lo más lentos, obsérvese el cambio del “if” por el “try”. Es obvio que para entenderlo se requiere un mínimo de inglés,
.
La otra solución
Casi se me olvidaba! Aunque la solución expuesta aquí es ORM-independente, existe una solución para aquellos que utilizan Doctrine basada en la utilización del behaviour SoftDelete, aunque eso es ya otra historia.
