In my app users can create and soft-delete payment gateways something like Paypal. Users of a website can connect to the gateway and make payments.
I've written the logic in a transaction also locking a matching record (on website
field) which was soft-deleted by the user because of race-condition. Read code for a better understanding.
Already exists (soft-deleted)
These gateways must be verified by admin, if the user submits different website
and description
fields in comparison to the matching record which was already verified, set is_verified
and rejection_reason
to null
is_verified
values:
true
verifiedfalse
rejectednull
pending
The Rule: Create if not exists, Restore if soft-deleted.
Somehow i think the controller can be simplified and refactored. Performance and Consistency both are important for me.
Migration
Schema::create('gateways', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')
->constrained()
->onUpdate('cascade')
->onDelete('cascade');
$table->string('title', 50);
$table->text('description');
$table->string('website')->unique();
$table->enum('wage', Gateway::getWageKeys());
$table->text('rejection_reason')->nullable();
$table->boolean('is_verified')->nullable()->default(null);
$table->boolean('is_blocked')->default(false);
$table->boolean('first_payment')->default(false);
$table->dateTime('free_until')->nullable();
$table->softDeletes();
$table->timestamps();
});
Controller
/**
* Create a new gateway.
*
* @param \App\Http\Requests\StoreGatewayRequest $request
* @return \App\Http\Resources\GatewayResource
*/
public function create(StoreGatewayRequest $request)
{
try {
$gateway = DB::transaction(function () use ($request) {
$gateway = $request->user()->gateways()->onlyTrashed()
->where('website', $request->website)->lockForUpdate()->first();
if (! $gateway) {
return $request->user()->gateways()->create($request->validated());
} else {
if ($gateway->is_blocked)
abort(452);
$gateway->fill($request->validated());
if ($gateway->isDirty('website') || $gateway->isDirty('description')) {
$gateway->rejection_reason = null;
$gateway->is_verified = null;
}
$gateway->deleted_at = null;
$gateway->save();
return $gateway;
}
});
} catch (QueryException $e) {
if ($e->errorInfo[1] === 1062) {
abort(409);
} else {
throw $e;
}
}
return new GatewayResource($gateway);
}