37

I am trying to identify when inserting a record using eloquent in Laravel when it throws an exception because of a unique field error.

The code I have so far is:

try {

    $result = Emailreminder::create(array(
                       'user_id' => Auth::user()->id,
                       'email' => $newEmail,
                       'token' => $token,
              ));

} catch (Illuminate\Database\QueryException $e) {
    return $e;
}

It throws an exception OK I just don't know what to do to identify it as a column duplicate error?

Thanks,

Gavin.

lukasgeiter
  • 147,337
  • 26
  • 332
  • 270
Gavin
  • 711
  • 1
  • 5
  • 13
  • First time adding code! Sorry! – Gavin Jan 10 '15 at 17:00
  • 1
    Since one of the major advantages of Eloquent is code that's not specific to any particular database, you probably don't want to allow this error to be thrown in the first place. It likely requires a different implementation to catch it based on what database you're using. The proper way to code this would be to first check if the record is in the database and handle it there. – TonyArra Jan 11 '15 at 14:41

3 Answers3

85

I'm assuming you use MySQL, it's probably different for other systems

Okay first, the error code for duplicate entry is 1062. And here's how you retrieve the error code from the exception:

catch (Illuminate\Database\QueryException $e){
    $errorCode = $e->errorInfo[1];
    if($errorCode == 1062){
        // houston, we have a duplicate entry problem
    }
}
Md Omar Faruk
  • 303
  • 3
  • 10
lukasgeiter
  • 147,337
  • 26
  • 332
  • 270
  • Excatly what I was after thanks. Although @TonyArra has a point about Eloquent being DB independent. The way I got round it was to use Laravels 'unique' validation rule. The problem with that it the table in question was a different one to the table the original information came from, so I found myself writing multiple validation statments which I found messy. So now I ask myself, do I go with Laravel's DB independence or accept the fact that the app probably will never use any other DB than MySQL and just tie it to that!! Hmmmm..... – Gavin Jan 12 '15 at 07:49
  • 5
    In Laravel 5.6 I had to add a leading slash before the classname, like so: \Illuminate\Database\QueryException – saswanb Aug 03 '18 at 17:52
  • It's not working for me, It's still recording the errors in the log file, bunch of them. – user2682025 May 11 '19 at 08:26
  • I added '\' before Illuminate\Database\QueryException, i.e. \Illuminate\Database\QueryException, since if not, it will just ignore the exception, now it worked. Thanks btw – jrran90 Jul 15 '20 at 04:02
  • [mpyw/laravel-retry-on-duplicate-key: Automatically retry non-atomic upsert operation when unique key constraints are violated.](https://github.com/mpyw/laravel-retry-on-duplicate-key) – mpyw Nov 22 '21 at 05:17
8

add this code inside class Handler (exception)

if($e instanceof QueryException){
        $errorCode = $e->errorInfo[1];          
        switch ($errorCode) {
            case 1062://code dublicate entry 
                return response([
                    'errors'=>'Duplicate Entry'
                ],Response::HTTP_NOT_FOUND);    
                break;
            case 1364:// you can handel any auther error
                return response([
                    'errors'=>$e->getMessage()
                ],Response::HTTP_NOT_FOUND);                        
                break;      
        }
     }
    ...
    return parent::render($request, $exception);
Amirouche Zeggagh
  • 3,428
  • 1
  • 25
  • 22
1

The long (a little bit weird) way, but works for any Databases

You can use Doctrine's ExceptionConverterInterface You should install package doctrine/dbal

And find the implementation of this interface by calling

app('db.connection')->getDoctrineConnection()->getDriver()->getExceptionConverter()

or

app(\Illuminate\Database\DatabaseManager::class)->connection()->getDoctrineConnection()->getDriver()->getExceptionConverter()

It applies Doctrine\DBAL\Driver\Exception as a first argument

You can get internal Doctrine\DBAL\Driver\PDO\Exception which implements this interface and instantiate it from your PODException

You can get your PDOException from QueryException this way:

$previous = $queryException->getPrevious();

if ($previous && ($previous instance \PDOException)) {
// ...
}

So, the final solution looks like:

$exceptionConverter = app('db.connection')->getDoctrineConnection()->getDriver()->getExceptionConverter()

$previous = $queryException->getPrevious();

if ($previous && ($previous instance \PDOException)) {
    $driverException = $exceptionConverter->convert($previous, null);

    if ($driverException instanceof \Doctrine\DBAL\Exception\UniqueConstraintViolationException) {
        // Our Exception is about non-unique value
    }
}

Please do not use this code in production :)