Вступ:
У Laravel зручно розміщувати бізнес-логіку у контролерах або моделях. Але що робити, коли логіка розростається? Вірне рішення — винести її в окремі сервіс-класи. Це дозволяє дотримуватись принципу SRP (Single Responsibility Principle) та робити код масштабованим і зручним для тестування.
Структура сервіс-класу
Розглянемо приклад сервісу для створення замовлення:
app/
├── Services/
│ └── OrderService.php
namespace App\Services; use App\Models\Order; use App\Models\User; use Illuminate\Support\Facades\DB; class OrderService { public function create( array $data , User $user ): Order { return DB::transaction( function () use ( $data , $user ) { $order = new Order(); $order ->user_id = $user ->id; $order ->total = $data [ 'total' ]; $order ->status = 'new' ; $order ->save(); // тут можна викликати події, надіслати email, тощо return $order ; }); } } |
Використання в контролері
namespace App\Http\Controllers; use App\Http\Requests\OrderRequest; use App\Services\OrderService; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Auth; class OrderController extends Controller { public function __construct( protected OrderService $orderService ) {} public function store(OrderRequest $request ): JsonResponse { $order = $this ->orderService->create( $request ->validated(), Auth::user()); return response()->json([ 'message' => 'Order created successfully.' , 'order' => $order , ]); } } |
Переваги такого підходу:
- Чиста архітектура: контролери “худнуть” і стають точками входу, а не логічним смітником.
- Легкість тестування: можна мокати залежності та тестувати сервіси окремо.
- Гнучкість: сервіс легко розширити без змін у контролерах.
- Повторне використання: один сервіс можна викликати з різних частин системи (REST, CLI, Queue).
Як протестувати сервіс
use App\Models\User; use App\Services\OrderService; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; class OrderServiceTest extends TestCase { use RefreshDatabase; public function test_it_creates_an_order() { $user = User::factory()->create(); $data = [ 'total' => 1000]; $service = new OrderService(); $order = $service ->create( $data , $user ); $this ->assertDatabaseHas( 'orders' , [ 'user_id' => $user ->id, 'total' => 1000, ]); } } |
Висновок:
Сервіс-класи — це не "overengineering", а шлях до стійкої та чистої архітектури. Вони допомагають будувати Laravel-проєкти, які зручно підтримувати і масштабувати.