Article not found

Showing database-errors

This article is also available in the following languages:
By CodingisFun
When the database raises an error Cake shows the text 'Please correct the errors below' without telling you what the error is.
This tutorial shows you a way to extend your application to show the user a understandable message after a database error.
Before a start I want to thank ben-xo. His article about detecting duplicate entries provided me the ideas and the basis for this tutorial.

To make your application show database errors you have to:
  1. Extend your app_model.php.
  2. Create an element to show database-errors.
  3. Extend your views with the element.
  4. Modify your controllers to show the database-errors.

The tutorial will show the code, and hopefully speaks for itself.

xtend your app_model.php.


<?php
/**
 * The class AppModel is extended with functions to show meaningfull database-errors. 
 * The class is able to work with any kind of database.
 * 
 * Put this file in the dir app or the contents of this file in app/app_model.php. 
 * 
 * The arrays with error-numbers and messages has to be extended!!
 * 
 */
class AppModel extends Model{
    
/**
 * Variables to hold information about the raised database-error. 
 *
 */
var $dbError;
var 
$dbErrorStr;
var 
$dbErrorNum

/**
 * User-understandable messages. 
 * The key is a MySQL number. 
 * Extend the array with al posible database errors!!
 */
    
var $dbErrorUserStr = array (    "1062" => 'Record with same key already exists.'
                                    
"1217" => 'Record is used in another table.', ); 
    
/**
 * Mapping between error-numbers and user-understandable messages, per database-type.
 * Error numbers for Postgress are FAKE. 
 * Extend the array with al posible database errors!!
 */
var $dbErrorMapping = array( "DboMysql" =>    array (    "1062" => '1062'
                                                       
"1217" => '1217', ),
                            
"DboPostgres" => array ("1111" => '1062'
                                                       
"8888" => '1217', )
                            ) ; 

/**
 * Override the function del(), 
 * Check database-error and call a function to invalidate the form. 
 */
    
function  del($id null$cascade true) { 
        
$returnval parent::del($id $cascade); 
        if(
false === $returnval) { 
            
$db =& ConnectionManager::getDataSource($this->useDbConfig);  
            
$this->dbError $db->lastError(); 
            list(
$this->dbErrorNum$this->dbErrorStr) = explode(":"$this->dbError);
            
            
$this->afterSaveFailed(); 
        }
        return 
$returnval

    }
    
/**
 * Override the function save(), 
 * Check database-error and call a function to invalidate the form. 
 */
    
function save($data null$validate true$fieldList = array()) { 
        
$returnval parent::save($data$validate$fieldList); 
        if(
false === $returnval) { 
            
$db =& ConnectionManager::getDataSource($this->useDbConfig);  
            
$this->dbError $db->lastError(); 
            list(
$this->dbErrorNum$this->dbErrorStr) = explode(":"$this->dbError);
            
            
$this->afterSaveFailed(); 
        } 
        return 
$returnval
    } 
    
/**
 * Calls invalidate()
 * Maybe overridden in model.
 * 
*/    
    
function afterSaveFailed() { 
        
$this->invalidate('DbError'); 
    } 
    
/**
 * returns the user understandable messages 
 * if the user understandable message is not defined it returns the originale database-message. 
 * 
*/    
    
function  dbErrorUserStr() { 
        
//    Determine the type of database...
        
$db =& ConnectionManager::getDataSource($this->useDbConfig);  
        
$key get_class($db); 
        if (
$key == 'DboMysqli'$key 'DboMysql';
        
//  Determine the number of the message using the mapping array. 
        
$dbErrorNum $this->dbErrorMapping[$key][$this->dbErrorNum]; 
        
        if ( isset(
$this->dbErrorUserStr$dbErrorNum ])){
            return 
$this->dbErrorUserStr$dbErrorNum ]; 
        } else { 
            return 
$this->dbError
        }
    }
    
    
}
?>

Create an element to show database-errors.


    <?php 
//    Suppress the echo if the variable is not defined...    
        
if ( isSet ($dbErrorUserStr)) {
            echo 
$html->tagErrorMsg$html->model '/DbError' $dbErrorUserStr);
            }
     ;
?>


Extend your views with the element.


<?php echo $this->renderElement('mainactions'); ?>
<h2>New Speed</h2>
<form action="<?php echo $html->url('/speeds/add'); ?>" method="post">
<div class="optional"> 
    <?php echo $form->labelTag('Speed/id''Id');?>
     <?php echo $html->input('Speed/id', array('size' => '60'));?>
    <?php echo $html->tagErrorMsg('Speed/id''Please enter the Id.');?>
</div>
<?php echo $this->renderElement('DbInvalidate'); ?>
<div class="submit">
    <?php echo $html->submit('Add');?>
</div>
</form>


Modify your controllers to show the database-errors.


    function edit($id = null) {
        $this->Speed->recursive = 0;
        if(empty($this->data)) {
            if(!$id) {
                $this->Session->setFlash('Invalid id for Speed');
                $this->redirect('/speeds/index');
            }
            $this->data = $this->Speed->read(null, $id);
        } else {
            $this->cleanUpFields();
            if($this->Speed->save($this->data)) {
                $this->Session->setFlash('The Speed has been saved');
                $this->redirect('/speeds/index');
            } else {
                $this->Session->setFlash('Please correct errors below.');
                $this->set('dbErrorUserStr', $this->Speed->dbErrorUserStr());
            }
        }
    }

    function delete($id = null) {
        if(!$id) {
            $this->Session->setFlash('Invalid id for Speed');
            $this->redirect('/speeds/index');
        }
        if($this->Speed->del($id)) {
            $this->Session->setFlash('The Speed deleted: id '.$id.'');
            $this->redirect('/speeds/index');
        }
        else
        {
            $this->Session->setFlash( "Record not deleted. " . $this->Speed->dbErrorUserStr() );
            $this->redirect($this->referer());
        }
    }




Comments

  • Posted 08/22/10 01:54:03 PM
    Hi
    i am a new user in cakePHP. my data are not saving in database. so to dispaly the database error i have used bellow code

    class AppModel extends Model{
    /**
    * Variables to hold information about the raised database-error.
    *
    */
    var $dbError;
    var $dbErrorStr;
    var $dbErrorNum;

    /**
    * User-understandable messages.
    * The key is a MySQL number.
    * Extend the array with al posible database errors!!
    */
    var $dbErrorUserStr = array ( "1062" => 'Record with same key already exists.',
    "1217" => 'Record is used in another table.', );

    /**
    * Mapping between error-numbers and user-understandable messages, per database-type.
    * Error numbers for Postgress are FAKE.
    * Extend the array with al posible database errors!!
    */
    var $dbErrorMapping = array( "DboMysql" => array ( "1062" => '1062',
    "1217" => '1217', ),
    "DboPostgres" => array ("1111" => '1062',
    "8888" => '1217', )
    ) ;

    /**
    * Override the function del(),
    * Check database-error and call a function to invalidate the form.
    */
    function del($id = null, $cascade = true) {
    $returnval = parent::del($id , $cascade);
    if(false === $returnval) {
    $db =& ConnectionManager::getDataSource($this->useDbConfig);
    $this->dbError = $db->lastError();
    list($this->dbErrorNum, $this->dbErrorStr) = explode(":", $this->dbError);

    $this->afterSaveFailed();
    }
    return $returnval;

    }

    /**
    * Override the function save(),
    * Check database-error and call a function to invalidate the form.
    */
    function save($data = null, $validate = true, $fieldList = array()) {
    $returnval = parent::save($data, $validate, $fieldList);
    if(false === $returnval) {

    $db =& ConnectionManager::getDataSource($this->useDbConfig);
    $this->dbError = $db->lastError();

    print $this->dbError;
    list($this->dbErrorNum, $this->dbErrorStr) = explode(":", $this->dbError);

    $this->afterSaveFailed();
    }
    return $returnval;
    }

    /**
    * Calls invalidate()
    * Maybe overridden in model.
    *
    */
    function afterSaveFailed() {
    $this->invalidate('DbError');
    }

    /**
    * returns the user understandable messages
    * if the user understandable message is not defined it returns the originale database-message.
    *
    */
    function dbErrorUserStr() {

    // Determine the type of database...
    $db =& ConnectionManager::getDataSource($this->useDbConfig);
    $key = get_class($db);
    if ($key == 'DboMysqli') $key = 'DboMysql';

    // Determine the number of the message using the mapping array.
    $dbErrorNum = $this->dbErrorMapping[$key][$this->dbErrorNum];

    if ( isset($this->dbErrorUserStr[ $dbErrorNum ])){
    return $this->dbErrorUserStr[ $dbErrorNum ];
    } else {
    return $this->dbError;
    }
    }
    }

    when i try to print $this->dbError, i am getting nothing

    Plese help me

  • Posted 02/21/10 07:42:22 PM
    Hadn't thought of this before, it works and really helps. thanx
  • Posted 02/09/10 08:11:42 AM
    Hi,

    Nice tutorial, works great.
    I got one suggestion:

    To prevent the "headers already sent" error when the $key does not exist, replace:

    Model Class:

    <?php 
    //  Determine the number of the message using the mapping array. 
            
    $dbErrorNum $this->dbErrorMapping[$key][$this->dbErrorNum];
    ?>

    with

    Model Class:

    <?php 
    //  Determine the number of the message using the mapping array. 
    $dbErrorNum = (isset($this->dbErrorMapping[$key][$this->dbErrorNum]) ? $this->dbErrorMapping[$key][$this->dbErrorNum] : null);
    ?>
  • Posted 04/22/08 10:28:03 PM
    Hi, I just wanted to know if the $db->lastError() method provides the last db error caused by the executing thread or if it provides the last db error independent from what thread caused it.

Comments are closed for articles over a year old