تمیز کردن کنترلرهای Laravel با Service Layer 🧼
خیلی وقتها کنترلرهای Laravel کمکم تبدیل میشوند به محل تجمع همهچیز: validation، منطق بیزنسی، queryهای Eloquent، ارسال notification و…
این یعنی:
• سختی در تست کردن 🧪
• کدهای تکراری در چند کنترلر
• سختی در تغییر منطق بیزنسی بدون شکستن endpointها
یکی از الگوهای ساده و کاربردی برای حل این مشکل در Laravel، استفاده از Service Layer است.
ایده اصلی ✨
هر action در کنترلر فقط کارهای زیر را انجام دهد:
• دریافت و validate ورودی
• صدا زدن یک Service (یا چند Service)
• برگرداندن Response مناسب (JSON / View)
تمام منطق بیزنسی داخل کلاسهای Service قرار میگیرد.
نمونه سناریو: ثبت سفارش 🛒
قبل از refactor (کنترلر چاق)
class OrderController extends Controller
{
public function store(Request $request)
{
$data = $request->validate([
'user_id' => ['required', 'exists:users,id'],
'items' => ['required', 'array'],
]);
$user = User::findOrFail($data['user_id']);
$order = DB::transaction(function () use ($user, $data) {
$order = $user->orders()->create();
foreach ($data['items'] as $item) {
$order->items()->create($item);
}
if ($user->isVip()) {
$order->applyDiscount(10);
}
return $order;
});
Notification::send($user, new OrderCreated($order));
return response()->json($order, 201);
}
}
کنترلر هم transaction دارد، هم business rule (Vip)، هم notification.
بعد از refactor با Service ✅
۱) ساخت Service:
namespace App\Services;
use App\Models\User;
use App\Notifications\OrderCreated;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Notification;
class OrderService
{
public function createOrderForUser(User $user, array $items)
{
$order = DB::transaction(function () use ($user, $items) {
$order = $user->orders()->create();
foreach ($items as $item) {
$order->items()->create($item);
}
if ($user->isVip()) {
$order->applyDiscount(10);
}
return $order;
});
Notification::send($user, new OrderCreated($order));
return $order;
}
}
۲) تزریق Service در کنترلر (Dependency Injection از Service Container):
use App\Models\User;
use App\Services\OrderService;
use Illuminate\Http\Request;
class OrderController extends Controller
{
public function __construct(private OrderService $orderService)
{
}
public function store(Request $request)
{
$data = $request->validate([
'user_id' => ['required', 'exists:users,id'],
'items' => ['required', 'array'],
]);
$user = User::findOrFail($data['user_id']);
$order = $this->orderService
->createOrderForUser($user, $data['items']);
return response()->json($order, 201);
}
}
مزایا 💡
• کنترلرها کوتاه و قابلخواندن
• قابلیت استفاده مجدد از منطق در Job، Command، Event Listener و…
• آمادهتر شدن برای تست واحد (Unit Test) روی Serviceها بهجای تستهای سنگین روی کنترلر
منابع پیشنهادی 📚
• مستندات Service Container در Laravel:
laravel.com/docs/container
این الگوی ساده اگر از همین امروز در پروژه لاراولتان اجرا شود، معماری تمیزتری میسازد و تغییرات بعدی را بسیار کمهزینهتر میکند 🌱
🔖 #Laravel #PHP #لاراول #Laravel #Service_Layer #Clean_Code #Controller #PHP #Architecture
👤 Developix
💎 Channel: @DevelopixLaravel