John Davidson

php - Best way or refactoring a create or restore controller in Laravel

0 comments
Message:


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 verified

  • false rejected

  • null 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 votes

Comments




© 2024 Hayatsk.info - Personal Blogs Platform. All Rights Reserved.
Create blog  |  Privacy Policy  |  Terms & Conditions  |  Contact Us