Search code examples
phplaraveleloquent

Laravel Eloquent, how to handle UNIQUE error?


I have a MySQL constraint to ensure unique on a composite key. When inserting a new record in my model Foo I get the expected error:

$foo = new Foo(['foo' => 42, 'bar => 1]);
$foo->save();

Error:

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '42' for key 'Unique'...

One solution to avoid this error is to query the model before inserting:

if (!Foo::where('foo', 42)->where('bar', 1)->first()) {
  $foo = new Foo(['foo' => 42, 'bar => 1]);
  $foo->save();
}

Another one would be to catch the exception when preg_match(/UNIQUE/, $e->message) is true.

Is there any better solution?

EDIT

I noticed that in Illuminate\Database\Eloquent\Builder Laravel does the double query anyway which is a bit sad:

public function findOrNew($id, $columns = ['*'])
{
    if (! is_null($model = $this->find($id, $columns))) {
        return $model;
    }

    return $this->newModelInstance();
}

Solution

  • In the general case you should be dealing with database errors using the error code and not any regex.

    In your particular case pre-querying or using a Laravel method that does that automatically for you, might be preferable if your intention is to overwrite/update existing data.

    If you want to generally anticipate an error and handle it you should do something like:

    try {
       $foo = new Foo(['foo' => 42, 'bar' => 1]);
       $foo->save();
    } catch (\Exception $e) { // It's actually a QueryException but this works too
       if ($e->getCode() == 23000) {
           // Deal with duplicate key error  
       }
    }
    

    Refer to https://dev.mysql.com/doc/refman/5.5/en/error-reference.html for an exhaustive list of error codes (but ideally you'd only need to deal with a couple of specific errors and under very specific circumstances.

    Lastly the SQL ON DUPLICATE KEY UPDATE might also work for you, however if you are doing this to silently ignore the new values then I suggest you do the error handling instead.