lockForUpdate() and sharedLock() are functions in Laravel Eloquent to set exclusive or shared locks (documentation here).
However, I couldnt find a nice syntax to apply this on a single, already instantiated Eloquent model. Consider the following example code:
DB::transaction(function() {
// Find the user with ID = 1.
$user = User::find(1);
$user->lockForUpdate()->update([
'balance' => $user->balance + 1
]);
// ... some more stuff happens here in the transaction
});
The code above will NOT work as expected. lockForUpdate() returns a new query builder here, and it will cause ALL users to have their balance incremented by one.
I want the balance property to be read-locked for the duration of this transaction, such that any other transaction that happens in parallel will not derail the account balance by calculating the wrong result. So how can I make sure that the balance property is locked while updating this user? I know I can call the following function but it seems a bit counter-intuitive to create a new query for this, which includes the $user variable as well:
$updated = User::query()->where('id', 1)->lockForUpdate()->update([
'balance' => $user->balance
]);
Note: I want to keep ->increment() and ->decrement() out of the equation here. I can't use these functions since I need Eloquent's updating/updated/saving/saved event hooks to fire properly (and they do not get triggered when using these functions). This is to be expected though, for more info see https://github.com/laravel/framework/issues/18802#issuecomment-593031090 .