I had the same problem too... i solved it with a trait with a litte different approach then from here:
https://gist.github.com/troatie/def0fba42fcfb70f873b7f033fbe255f
I explain in short why this approach. I do not like regular database locks for events that rarely happen, we have an exception on our busy server once every two days due to the firstOrCreate problematic. To make a lock and delete a lock on every create for such a rare incident... well... This approach takes care when it happens and because i am paranoid i take more than double care and thats it.
<?php
namespace App\Traits;
use Exception;
/**
* Trait OrCreateTrait
*/
trait OrCreateTrait
{
/**
* @param array $attributes
* @param array $values
*
* @return mixed
*/
public static function updateOrCreate(array $attributes, array $values = [])
{
return static::tryManyTimes(function () use ($attributes, $values) {
return (new static)->newQuery()->updateOrCreate($attributes, $values);
});
}
/**
* @param array $attributes
* @param array $values
*
* @return mixed
*/
public static function firstOrCreate(array $attributes, array $values = [])
{
return static::tryManyTimes(function () use ($attributes, $values) {
return (new static)->newQuery()->firstOrCreate($attributes, $values);
});
}
/**
* @param callable $callback
*
* @return mixed
*/
private static function tryManyTimes(callable $callback)
{
try {
$output = $callback();
} catch (Exception $e) {
try {
$output = $callback();
} catch (Exception $e) {
usleep(10000);
try {
$output = $callback();
} catch (Exception $e) {
usleep(100000);
try {
$output = $callback();
} catch (Exception $e) {
usleep(250000);
try {
$output = $callback();
} catch (Exception $e) {
$output = null;
}
}
}
}
}
return $output;
}
}