简体   繁体   中英

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();
}

You can use the firstOrCreate method:

$foo = Foo::firstOrCreate(['foo' => 42, 'bar' => 1]);

This will check if the record exists in the database before creating it. If it exists, it will return the record.

For more information: https://laravel.com/docs/5.8/eloquent#inserting-and-updating-models

laravel为您提供了firstOrCreate函数,它首先检查数据库中是否存在该值,然后您还要使用updateOrCreate函数,以便更新某些值,但最重要的是,如果您只想处理唯一记录,那么请检查验证规则,您将执行FormRequest并向该字段添加唯一规则,在该laravel之后为您提供错误消息,以便您在客户端处理错误

The verification need to be done at Controller level (like laravel Validator) and it's better to do a count() instead of the first() .

There are different solution depending on what you wanna do when the entity exists in the database. For example you can do updateOrCreate()

$foo = App\Foo::updateOrCreate(['foo' => 42, 'bar' => 1]);

or throw an exception

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM