In this lesson, our goal is to refactor Controllers into Service classes.
Let's look into this diagram. The problem is that two restaurant controllers are implementing the same business logic highlighted in the red area of the illustration.
In the best-case scenario, we want to avoid duplicate code and merge that logic into a single entity.
If we have changes to business logic, it also brings the benefit of not updating it in multiple places.
RestaurantService Class
Create a new RestaurantService
class.
app/Services/RestaurantService.php
namespace App\Services; use App\Enums\RoleName;use App\Models\Restaurant;use App\Models\Role;use App\Models\User;use App\Notifications\RestaurantOwnerInvitation;use Illuminate\Support\Facades\DB; class RestaurantService{ public function createRestaurant(array $attributes): Restaurant { return DB::transaction(function () use ($attributes) { $user = User::create([ 'name' => $attributes['owner_name'], 'email' => $attributes['email'], 'password' => '', ]); $user->roles()->sync(Role::where('name', RoleName::VENDOR->value)->first()); $restaurant = $user->restaurant()->create([ 'city_id' => $attributes['city_id'], 'name' => $attributes['restaurant_name'], 'address' => $attributes['address'], ]); $user->notify(new RestaurantOwnerInvitation($attributes['restaurant_name'])); return $restaurant; }); } public function updateRestaurant(Restaurant $restaurant, array $attributes): Restaurant { $restaurant->update([ 'city_id' => $attributes['city_id'], 'name' => $attributes['restaurant_name'], 'address' => $attributes['address'], ]); return $restaurant; }}
Here we have implemented the createRestaurant
and updateRestaurant
methods that we can call from controllers.
Web Controller
Now we can use constructor property promotion on the Controller to inject RestaurantService
and imported classes we no longer need there.
app/Http/Controllers/Admin/RestaurantController.php
namespace App\Http\Controllers\Admin; use App\Enums\RoleName; use App\Http\Controllers\Controller;use App\Http\Requests\Admin\StoreRestaurantRequest;use App\Http\Requests\Admin\UpdateRestaurantRequest;use App\Models\City;use App\Models\Restaurant;use App\Models\Role; use App\Models\User; use App\Notifications\RestaurantOwnerInvitation; use App\Services\RestaurantService;use Illuminate\Http\RedirectResponse;use Illuminate\Support\Facades\DB; use Inertia\Inertia;use Inertia\Response; class RestaurantController extends Controller{ public function __construct(public RestaurantService $restaurantService) { } // ...
Update the store
method and call the createRestaurant
method on injected service.
app/Http/Controllers/Admin/RestaurantController.php
public function store(StoreRestaurantRequest $request): RedirectResponse{ DB::transaction(function () use ($validated) { $user = User::create([ 'name' => $validated['owner_name'], 'email' => $validated['email'], 'password' => '', ]); $user->roles()->sync(Role::where('name', RoleName::VENDOR->value)->first()); $user->restaurant()->create([ 'city_id' => $validated['city_id'], 'name' => $validated['restaurant_name'], 'address' => $validated['address'], ]); $user->notify(new RestaurantOwnerInvitation($validated['restaurant_name'])); }); $this->restaurantService->createRestaurant( $request->validated() ); return to_route('admin.restaurants.index');}
In the same way, we update the update
method.
app/Http/Controllers/Admin/RestaurantController.php
public function update(UpdateRestaurantRequest $request, Restaurant $restaurant): RedirectResponse{ $validated = $request->validated(); $restaurant->update([ 'city_id' => $validated['city_id'], 'name' => $validated['restaurant_name'], 'address' => $validated['address'], ]); $this->restaurantService->updateRestaurant( $restaurant, $request->validated() ); return to_route('admin.restaurants.index') ->withStatus('Restaurant updated successfully.');}
API Controller
Then we apply the same changes to the API controller.
app/Http/Controllers/Api/V1/Admin/RestaurantController.php
use App\Enums\RoleName; use App\Http\Controllers\Controller;use App\Http\Requests\Admin\StoreRestaurantRequest;use App\Http\Requests\Admin\UpdateRestaurantRequest;use App\Http\Resources\Api\V1\Admin\RestaurantCollection;use App\Http\Resources\Api\V1\Admin\RestaurantResource;use App\Models\Restaurant;use App\Models\Role; use App\Models\User; use App\Notifications\RestaurantOwnerInvitation; use App\Services\RestaurantService;use Illuminate\Http\JsonResponse;use Illuminate\Http\Response;use Illuminate\Support\Facades\DB; class RestaurantController extends Controller{ public function __construct(public RestaurantService $restaurantService) { } // ...
app/Http/Controllers/Api/V1/Admin/RestaurantController.php
public function store(StoreRestaurantRequest $request): RestaurantResource{ $validated = $request->validated(); $restaurant = DB::transaction(function () use ($validated) { $user = User::create([ 'name' => $validated['owner_name'], 'email' => $validated['email'], 'password' => '', ]); $user->roles()->sync(Role::where('name', RoleName::VENDOR->value)->first()); $user->restaurant()->create([ 'city_id' => $validated['city_id'], 'name' => $validated['restaurant_name'], 'address' => $validated['address'], ]); $user->notify(new RestaurantOwnerInvitation($validated['restaurant_name'])); }); $restaurant = $this->restaurantService->createRestaurant( $request->validated() ); return new RestaurantResource($restaurant);}
app/Http/Controllers/Api/V1/Admin/RestaurantController.php
public function update(UpdateRestaurantRequest $request, Restaurant $restaurant): JsonResponse{ $validated = $request->validated(); $restaurant->update([ 'city_id' => $validated['city_id'], 'name' => $validated['restaurant_name'], 'address' => $validated['address'], ]); $restaurant = $this->restaurantService->updateRestaurant( $restaurant, $request->validated() ); return (new RestaurantResource($restaurant)) ->response() ->setStatusCode(Response::HTTP_ACCEPTED);}
No comments or questions yet...