← Назад к вопросам

Куда следует вынести логику из контроллера в Laravel?

2.0 Middle🔥 181 комментариев
#Фреймворки#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Куда выносить логику из контроллера в Laravel

В Laravel контроллеры должны оставаться тонкими (thin controllers), отвечая только за обработку HTTP-запросов и возврат ответов. Вынос логики из контроллеров улучшает поддерживаемость, тестируемость и переиспользование кода. Вот основные места для выноса логики:

1. Сервисные классы (Service Classes)

Сервисы инкапсулируют бизнес-логику, не зависящую от фреймворка. Их можно разместить в app/Services/.

namespace App\Services;

class PaymentService
{
    public function processPayment($order, $data)
    {
        // Логика обработки платежа
    }
}

Использование в контроллере:

public function store(PaymentRequest $request, PaymentService $service)
{
    $service->processPayment($order, $request->validated());
    return redirect()->route('orders.index');
}

2. Действия (Actions) или Jobs

Для сложных операций, особенно асинхронных, используйте Job-ы (очереди) или Action-классы (паттерн "единая ответственность").

namespace App\Jobs;

class ProcessOrderJob implements ShouldQueue
{
    public function handle()
    {
        // Длительная логика обработки заказа
    }
}

3. Репозитории (Repositories)

Репозитории абстрагируют работу с данными, отделяя логику от Eloquent. Паттерн особенно полезен для сложных запросов.

namespace App\Repositories;

class OrderRepository
{
    public function getActiveOrdersWithProducts(User $user)
    {
        return $user->orders()
                    ->where('status', 'active')
                    ->with('products')
                    ->get();
    }
}

4. Формы запросов (Form Requests)

Валидацию и авторизацию следует выносить в Form Request-классы.

namespace App\Http\Requests;

class UpdateProfileRequest extends FormRequest
{
    public function authorize()
    {
        return $this->user()->can('update', $this->profile);
    }

    public function rules()
    {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email,' . $this->user()->id,
        ];
    }
}

5. Обсерверы (Observers) и События (Events)

Для реагирования на изменения моделей используйте Observers, а для декомпозиции сложных процессов — Events и Listeners.

namespace App\Observers;

class OrderObserver
{
    public function created(Order $order)
    {
        // Логика после создания заказа
    }
}

6. Трейты (Traits) и Хелперы (Helpers)

Повторяющуюся логику можно выносить в Traits или кастомные Helper-классы.

namespace App\Traits;

trait HandlesMediaUpload
{
    protected function uploadMedia($file, $path)
    {
        // Логика загрузки файлов
    }
}

Критерии выбора подхода

  • Сложность логики: Для простых операций подойдут Service-классы, для фоновых задач — Jobs.
  • Переиспользование: Если логика нужна в нескольких контроллерах, используйте Services или Repositories.
  • Асинхронность: Долгие операции (отправка писем, обработка изображений) выносите в Jobs.
  • Реактивность: При реакции на действия с моделями применяйте Observers или Events.
  • Валидация/Авторизация: Всегда используйте Form Requests для соответствующих задач.

Практический пример рефакторинга

Было (жирный контроллер):

public function store(Request $request)
{
    $request->validate([...]);
    $order = Order::create([...]);
    foreach ($request->products as $product) {
        $order->products()->attach($product);
    }
    Mail::to($request->user())->send(new OrderCreated($order));
    return redirect()->route('orders.show', $order);
}

Стало (разделенная логика):

public function store(StoreOrderRequest $request, OrderService $service)
{
    $order = $service->createOrder($request->validated());
    ProcessOrderJob::dispatch($order);
    return redirect()->route('orders.show', $order);
}

Преимущества разделения

  • Упрощение тестирования: Логику в Service-классах легко тестировать отдельно от HTTP-слоя.
  • Гибкость архитектуры: Легко менять реализации (например, перейти с Eloquent на другую БД).
  • Читаемость кода: Контроллеры становятся понятными и сфокусированными на потоке запроса.
  • Соблюдение SRP: Каждый класс отвечает за одну задачу.

Вынос логики из контроллеров — фундаментальный принцип чистой архитектуры в Laravel, который прямо влияет на долгосрочное качество проекта. Начните с Form Requests и Service-классов, затем добавляйте Repositories и Jobs по мере роста сложности приложения.