Ответ
В Laravel я строго следую принципу "тонких контроллеров" и выношу логику в специализированные классы. Вот основные места и примеры из моего опыта:
1. Сервисные классы (Service Classes) — для бизнес-логики.
Размещаю в app/Services/. Контроллер делегирует им основную работу.
Пример: Сервис для регистрации пользователя.
// app/Services/UserRegistrationService.php
namespace AppServices;
use AppModelsUser;
use AppNotificationsWelcomeNotification;
class UserRegistrationService
{
public function register(array $data): User
{
// Вся бизнес-логика создания пользователя
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
// Отправка welcome-email через очередь
$user->notify(new WelcomeNotification());
// Логирование события
Log::info('New user registered', ['user_id' => $user->id]);
return $user;
}
}
// В контроллере
public function store(RegisterRequest $request, UserRegistrationService $service)
{
$user = $service->register($request->validated());
return response()->json($user, 201);
}
2. Action-классы или Jobs — для отдельных операций. Идеально для инкапсуляции сложной логики, которую можно повторно использовать.
php artisan make:action/CreateNewOrderAction
3. Form Request Validation — для валидации.
Выношу правила валидации из контроллера в отдельные классы app/Http/Requests/.
4. Репозитории — для абстракции работы с данными. Использую, когда нужен сложный доступ к данным или замена источника (БД, API, кэш).
// app/Repositories/UserRepository.php
class UserRepository
{
public function findActiveWithLatestOrder(int $userId): ?User
{
return User::where('id', $userId)
->where('is_active', true)
->with(['latestOrder'])
->first();
}
}
5. Events & Listeners — для реактивной логики.
Например, при создании заказа (OrderCreated) запускаются слушатели на отправку чека, обновление инвентаря и нотификацию менеджера.
Итог: Мой контроллер становится маршрутизатором (router), который:
- Валидирует входящий запрос (через Form Request).
- Вызывает нужный сервис, action или job.
- Возвращает HTTP-ответ. Это делает код контроллеров предсказуемым, тестируемым и легким для поддержки.
Ответ 18+ 🔞
Э, слушай, а ведь это правильный подход, ёпта! Тонкие контроллеры — это как раз то, что доктор прописал, когда проект начинает жрать оперативку и терпения ноль ебать. Сам так делаю, иначе потом разгребать этот пиздец — одно мучение.
Вот смотри, куда я обычно всю эту логику выношу, чтобы контроллер был просто красивой обёрткой, а не монстром на 300 строк.
1. Сервисы (Service Classes) — для всей этой вашей бизнес-хуйни.
Кидаю их в app/Services/. Контроллеру остаётся только позвать нужного мужика и отдать результат.
Пример: Сервис, который юзера регистрирует.
// app/Services/UserRegistrationService.php
namespace AppServices;
use AppModelsUser;
use AppNotificationsWelcomeNotification;
class UserRegistrationService
{
public function register(array $data): User
{
// Вся основная движуха по созданию пользователя тут
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
// Welcome-email в очередь, чтобы не тормозить
$user->notify(new WelcomeNotification());
// Логируем, что вот, мол, новый лох появился
Log::info('New user registered', ['user_id' => $user->id]);
return $user;
}
}
// А в контроллере — красота, одна строчка по сути
public function store(RegisterRequest $request, UserRegistrationService $service)
{
$user = $service->register($request->validated());
return response()->json($user, 201);
}
2. Action-классы или Jobs — для чётко выделенных действий. Идеально, когда у тебя есть какая-то хитрая жопа операция, которую можно переиспользовать. Создаёшь — и потом тыкаешь, где надо.
php artisan make:action/CreateNewOrderAction
3. Form Request Validation — чтобы не засирать контроллер проверками.
Выносишь все эти $request->validate([...]) в отдельные классы в app/Http/Requests/. Контроллер сразу получает уже провалидированные и чистые данные, доверия ебать ноль к вводу, но зато удобно.
4. Репозитории — когда работа с данными становится сложнее, чем «найди по id».
Если тебе нужно не просто User::find(1), а с кучей условий, джойнов и чтобы ещё и из кэша доставалось — это сюда.
// app/Repositories/UserRepository.php
class UserRepository
{
public function findActiveWithLatestOrder(int $userId): ?User
{
return User::where('id', $userId)
->where('is_active', true)
->with(['latestOrder'])
->first();
}
}
5. Events & Listeners — когда нужно, чтобы одно действие запускало кучу других.
Создал заказ (OrderCreated) — и поехало: слушатель на отправку чека, слушатель на списание остатков, слушатель, который бухгалтерию в телеге материт. Красота, всё развязано.
Итог-то какой? Мой контроллер превращается в этакого диспетчера, который:
- Говорит: «Данные валидны? А, Form Request уже проверил. Окей».
- Кричит в нужную сторону: «Эй,
UserRegistrationService, сделай тут че надо!». - Получает результат и отдаёт его наружу в виде JSON или вьюхи.
Получается чисто, понятно и тестируется на ура. А иначе потом сам от себя охуеешь, когда через полгода придётся в этот контроллер-монстр лезть.