Приведи пример принципа единственной ответственности (SRP) в Laravel.

«Приведи пример принципа единственной ответственности (SRP) в Laravel.» — вопрос из категории Laravel, который задают на 24% собеседований PHP Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Принцип единственной ответственности (SRP) гласит, что класс должен иметь только одну причину для изменения. В Laravel это часто нарушается, когда контроллеры берут на себя слишком много. Рассмотрим пример с обработкой заказа.

Проблемный код (нарушение SRP):

// app/Http/Controllers/OrderController.php
class OrderController extends Controller
{
    public function store(Request $request)
    {
        // Валидация
        $validated = $request->validate([...]);

        // Создание заказа (бизнес-логика)
        $order = Order::create($validated);

        // Отправка email (логика уведомлений)
        Mail::to($order->user)->send(new OrderCreated($order));

        // Генерация PDF-счета (логика отчетности)
        $pdf = PDF::loadView('invoices.order', ['order' => $order]);
        $pdf->save(storage_path('app/invoices/' . $order->id . '.pdf'));

        return redirect()->route('orders.show', $order);
    }
}

Здесь контроллер отвечает за HTTP-запрос, бизнес-логику, отправку почты и генерацию файлов.

Решение с SRP:

// 1. Контроллер отвечает только за HTTP
// app/Http/Controllers/OrderController.php
class OrderController extends Controller
{
    public function __construct(private CreateOrderAction $createOrder) {}

    public function store(StoreOrderRequest $request)
    {
        $order = $this->createOrder->execute($request->validated());

        return redirect()->route('orders.show', $order);
    }
}

// 2. Отдельный класс для бизнес-логики создания заказа (Action/Service)
// app/Actions/CreateOrderAction.php
class CreateOrderAction
{
    public function __construct(
        private SendOrderCreatedNotification $sendNotification,
        private GenerateOrderInvoice $generateInvoice
    ) {}

    public function execute(array $data): Order
    {
        $order = Order::create($data);

        $this->sendNotification->handle($order);
        $this->generateInvoice->handle($order);

        return $order;
    }
}

// 3. Отдельный класс для отправки уведомлений
// app/Actions/SendOrderCreatedNotification.php
class SendOrderCreatedNotification
{
    public function handle(Order $order): void
    {
        Mail::to($order->user)->send(new OrderCreated($order));
    }
}

// 4. Отдельный класс для генерации счета
// app/Actions/GenerateOrderInvoice.php
class GenerateOrderInvoice
{
    public function handle(Order $order): void
    {
        $pdf = PDF::loadView('invoices.order', ['order' => $order]);
        $pdf->save(storage_path('app/invoices/' . $order->id . '.pdf'));
    }
}

Теперь каждый класс имеет одну четкую ответственность, что упрощает тестирование, поддержку и повторное использование кода.