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

Ответ

Принцип единственной ответственности (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'));
    }
}

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

Ответ 18+ 🔞

Ёпта, смотри, какой прикол. Есть такая штука — принцип единственной ответственности. По-русски это звучит как «не пытайся впихнуть невпихуемое в один класс». А в Laravel, блядь, это нарушают так часто, что уже волнение ебать. Особенно эти контроллеры, которые начинают делать всё на свете, как последние распиздяи.

Вот смотри на этот пиздопроебибный код. Чувак взял и запихнул в контроллер валидацию, создание заказа, отправку мыла и ещё генерацию PDF-счёта. Ну ты представляешь? Это же манда с ушами получается!

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, за бизнес, за почту и за файлы. Доверия ебать ноль к такому коду. Захотел поменять логику отправки уведомлений — придётся лезть в контроллер и рисковать всё сломать. Это же хитрая жопа, которая тебя потом обязательно подставит.

А теперь смотри, как надо делать по-человечески. Разделяем всё на отдельные сущности, чтобы у каждой была одна чёткая задача.

Сначала делаем контроллер, который только за HTTP отвечает. Его дело — принять запрос, отдать данные в бизнес-логику и вернуть ответ. Всё, да похуй на остальное.

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);
    }
}

Потом создаём отдельный класс для самой операции создания заказа. Пусть он рулит процессом, но не делает всё сам, а делегирует.

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;
    }
}

А дальше — ёперный театр — создаём два маленьких и понятных класса. Один только письма шлёт, другой только PDF-ки генерит.

class SendOrderCreatedNotification
{
    public function handle(Order $order): void
    {
        Mail::to($order->user)->send(new OrderCreated($order));
    }
}
class GenerateOrderInvoice
{
    public function handle(Order $order): void
    {
        $pdf = PDF::loadView('invoices.order', ['order' => $order]);
        $pdf->save(storage_path('app/invoices/' . $order->id . '.pdf'));
    }
}

И что получается? Каждый класс теперь — сам от себя охуел от своей простоты. Тестировать их — одно удовольствие. Менять что-то — не страшно. Поддерживать — легко. Это же ебушки-воробушки по сравнению с той свалкой, что была в начале.

Запомни, чувак: если твой класс делает больше одной вещи, он уже полупидор. Дели ответственности, и будет тебе счастье.