Search code examples
phpdatabasesymfonyexceptiondoctrine-orm

Catch db error with Doctrine\DBAL\Exception


I want to catch any error (ie: foreign key error) from an insert statement or any other. How can I achieve that with use Doctrine\DBAL\Exception?

I have this when I do the insert:

$db->beginTransaction();
try {
  $db->insert('bbc5.produccionpry',$datos);
  $datos['propryid'] = $db->lastInsertId();
  $db->commit();
  // $db = null;
  $resp[] = $datos;
} catch (Exception $e) {
  $error = array_merge($error, array('error' => $e->errorInfo()));
  $db->rollback();
  throw $e;
}

But, that doesn't prevent the concrete5 to return a website telling about the error, so, I don't want that website to be shown, I want to catch the error in an array() in order to return it via echo json_encode($error)

I'm not using the controller for a page, I'm using it for managing RESTful calls from my JavaScript App with this code:

return fetch(`/scamp/index.php/batchprodpry/${maq}`, { 
  method: 'POST', 
  credentials: 'same-origin', 
  headers: { 
    'Accept': 'application/json', 
    'Content-Type': 'application/json' 
  }, 
  body: JSON.stringify(this.state.a) 
})

I'm using ReactJS and Concrete5.7


Solution

  • Don't throw the exception.

    Instead of throwing the exception, just get the exceptions message $e->getMessage() from the DBALException $e object and encode it as a JSON string. Important: Put an exit; after the echo to assure that no further code is executed.

    use Doctrine\DBAL\DBALException;
    
    try {
        $db->insert('bbc5.produccionpry',$datos);
        $datos['propryid'] = $db->lastInsertId();
        $db->commit();
        $resp[] = $datos;
    } 
    catch(DBALException $e){
        $db->rollback();
        echo \Core::make('helper/json')->encode($e->getMessage());
        exit;
    }
    

    If this code is inside a page controller you could do this:

    try {
        $db->insert('bbc5.produccionpry',$datos);
        $datos['propryid'] = $db->lastInsertId();
        $db->commit();
        $resp[] = $datos;
    }
    catch(DBALException $e){
        $this->error->add($e->getMessage());
    }
    
    if($this->error->has()) {
        // All variables that have been set in the view() method will be set again.
        // That is why we call the view method again
        $this->view();
        return;
    }
    

    And concrete5 will take care of displaying an appropriate error message.

    Or you could save the $e->getMesssage() in the session and call it inside the view:

    $session = \Core::make('session');
    // ...
    catch(Exception $e){
        $session->set('error', $e->getMessage());
    }
    

    And in the view:

    // html
    <?php 
    $session = \Core::make('session');
    if($session->has('error')) {
        $m = $session->get('error');
        ?>
        <div id="session_error" class="alert alert-danger">
            <a href="#" class="close">&times;</a>
    
            <div id="session_error_msg">
                <?php  print $m ?>
            </div>
        </div>
    <?php 
    }
    $session->remove('error');
    ?>
    //html