In my app users can create and soft-delete 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 my code is not ok and i'm overthinking or anything else (as a perfectionist + imposter syndrome).
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);
}
My Answer:
>increments('id');
$table->string('name');
$table->string('website');
$table->string('description');
$table->boolean('is_verified')->nullable();
$table->string('rejection_reason')->nullable();
$table->softDeletes();
$table->timestamps();
});
_Controller_Method:_
php
public function createOrRestore(Request $request)
{
$gateway = Gateway::withTrashed()->where('website', $request->website)->first();
if ($gateway) {
if ($gateway->trashed()) {
$gateway->restore();
$gateway->update([
'description' => $request->description,
'is_verified' => null,
'rejection_reason' => null
]);
return response()->json(['message' => 'Gateway restored successfully']);
} else {
if ($gateway->is_verified === true) {
return response()->json(['message' => 'Gateway already verified']);
} else {
$gateway->update([
'description' => $request->description,
'is_verified' => null,
'rejection_reason' => null
]);
return response()->json(['message' => 'Gateway updated successfully']);
}
}
} else {
$gateway = Gateway::create([
'name' => $request->name,
'website' => $request->website,
'description' => $request->description,
'is_verified' => null,
'rejection_reason' => null
]);
return response()->json(['message' => 'Gateway created successfully']);
}
}
_Improvements:_
1. You can simplify the logic by checking if the gateway exists and is soft-deleted in one condition.
2. You can use Laravel's `updateOrCreate` method to handle both creation and updating of the gateway in a single line of code.
3. You can move the logic for setting `is_verified` and `rejection_reason` to a separate method to keep the controller method clean and readable.
_Refactored Controller Method:_
php
public function createOrRestore(Request $request)
{
$gateway = Gateway::withTrashed()->where('website', $request->website)->first();
if ($gateway) {
if ($gateway->trashed()) {
$gateway->restore();
}
$this->updateGateway($gateway, $request);
return response()->json(['message' => $gateway->wasRecentlyCreated ? 'Gateway created successfully' : 'Gateway updated successfully']);
} else {
$gateway = Gateway::create([
'name' => $request->name,
'website' => $request->website,
'description' => $request->description,
'is_verified' => null,
'rejection_reason' => null
]);
return response()->json(['message' => 'Gateway created successfully']);
}
}
private function updateGateway($gateway, $request)
{
$gateway->update([
'description' => $request->description,
'is_verified' => null,
'rejection_reason' => null
]);
}
By refactoring the code in this way, you can simplify the logic and make it easier to understand and maintain.
Rate this post
3 of 5 based on 1711 votesComments