Erik Berkun-Drevnig Erik Berkun-Drevnig - 3 months ago 23
PHP Question

Can firstOrCreate be treated atomically in Laravel?

I have an API that another application hooks into to upload some user data. Each peice of data belongs to a user and the user's email address is included in each API request. If the email doesn't exist in the users table then a new user is created, otherwise the new data will be added under the existing user.

I need to if

firstOrCreate
will be guarenteed to return a
User
object if two API request are made back to back. In my current code, I am querying the database and if it user doesn't exist I create one. But I am having an issue where if two API calls are made back to back, I get two inserts into the database (two users with the same email). I have since added a unique constraint on the email column but I want to be sure that if I use
firstOrCreate
I will be able to proceed after that knowing that I either have the existing user or I have successfully created on.

Answer

Laravel firstOrCreate does not have atomic guards. But, since you have a UNIQUE constraint on the column, the database will enforce atomicity for you. Thus:

$params = [ 'email' => 'foo@example.com', /* ... */ ];
try {
    $user = User::firstOrCreate($params);
} catch (\Illuminate\Database\QueryException $ex) {
    $user = User::firstOrCreate($params);
}

If this fails the first time, then you've got contention. So it retries the attempt. Presumably, the second attempt will return the first record matching. However, be warned that the Illuminate\Database\QueryException may occur for a multitude of reasons, unique key violations being one of them. Thus the second attempt may be made but still not actually return a user.