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

Приведи пример единственной ответственности в Laravel

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

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

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

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

Пример принципа единственной ответственности (Single Responsibility Principle) в Laravel

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

Проблемный код без SRP

Рассмотрим типичный контроллер в Laravel, который нарушает SRP, так как выполняет множество задач:

namespace App\Http\Controllers;

use App\Models\Order;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\Mail\OrderConfirmation;

class OrderController extends Controller
{
    public function store(Request $request)
    {
        // 1. Валидация данных
        $validated = $request->validate([
            'product_id' => 'required|exists:products,id',
            'quantity' => 'required|integer|min:1',
            'user_id' => 'required|exists:users,id'
        ]);
        
        // 2. Бизнес-логика (проверка наличия товара)
        $product = Product::find($validated['product_id']);
        if ($product->stock < $validated['quantity']) {
            return response()->json(['error' => 'Недостаточно товара на складе'], 400);
        }
        
        // 3. Создание заказа
        $order = Order::create([
            'user_id' => $validated['user_id'],
            'product_id' => $validated['product_id'],
            'quantity' => $validated['quantity'],
            'total_price' => $product->price * $validated['quantity'],
            'status' => 'pending'
        ]);
        
        // 4. Обновление остатков товара
        $product->decrement('stock', $validated['quantity']);
        
        // 5. Отправка email уведомления
        $user = User::find($validated['user_id']);
        Mail::to($user->email)->send(new OrderConfirmation($order));
        
        // 6. Логирование
        \Log::info('Заказ создан', ['order_id' => $order->id, 'user_id' => $user->id]);
        
        return response()->json($order, 201);
    }
}

Этот контроллер нарушает SRP, потому что он:

  • Валидирует данные
  • Содержит бизнес-логику
  • Работает с базой данных
  • Отправляет письма
  • Логирует действия

Рефакторинг с соблюдением SRP

1. Создание FormRequest для валидации

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreOrderRequest extends FormRequest
{
    public function authorize()
    {
        return true;
    }
    
    public function rules()
    {
        return [
            'product_id' => 'required|exists:products,id',
            'quantity' => 'required|integer|min:1',
            'user_id' => 'required|exists:users,id'
        ];
    }
}

2. Создание сервиса для бизнес-логики

namespace App\Services;

use App\Models\Order;
use App\Models\Product;
use App\Models\User;
use App\Mail\OrderConfirmation;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Log;

class OrderService
{
    public function createOrder(array $data): Order
    {
        $product = Product::find($data['product_id']);
        
        $this->checkStockAvailability($product, $data['quantity']);
        
        $order = $this->createOrderRecord($product, $data);
        
        $this->updateProductStock($product, $data['quantity']);
        
        $this->sendConfirmationEmail($order);
        
        $this->logOrderCreation($order);
        
        return $order;
    }
    
    private function checkStockAvailability(Product $product, int $quantity): void
    {
        if ($product->stock < $quantity) {
            throw new \Exception('Недостаточно товара на складе');
        }
    }
    
    private function createOrderRecord(Product $product, array $data): Order
    {
        return Order::create([
            'user_id' => $data['user_id'],
            'product_id' => $data['product_id'],
            'quantity' => $data['quantity'],
            'total_price' => $product->price * $data['quantity'],
            'status' => 'pending'
        ]);
    }
    
    private function updateProductStock(Product $product, int $quantity): void
    {
        $product->decrement('stock', $quantity);
    }
    
    private function sendConfirmationEmail(Order $order): void
    {
        $user = User::find($order->user_id);
        Mail::to($user->email)->send(new OrderConfirmation($order));
    }
    
    private function logOrderCreation(Order $order): void
    {
        Log::info('Заказ создан', ['order_id' => $order->id, 'user_id' => $order->user_id]);
    }
}

3. Упрощенный контроллер

namespace App\Http\Controllers;

use App\Http\Requests\StoreOrderRequest;
use App\Services\OrderService;
use Illuminate\Http\JsonResponse;

class OrderController extends Controller
{
    protected $orderService;
    
    public function __construct(OrderService $orderService)
    {
        $this->orderService = $orderService;
    }
    
    public function store(StoreOrderRequest $request): JsonResponse
    {
        try {
            $order = $this->orderService->createOrder($request->validated());
            return response()->json($order, 201);
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 400);
        }
    }
}

Дальнейшее разделение ответственности

Для большего соблюдения SRP можно пойти дальше:

Создание репозитория для работы с данными:

namespace App\Repositories;

use App\Models\Order;
use App\Models\Product;

class OrderRepository
{
    public function create(array $data): Order
    {
        return Order::create($data);
    }
}

class ProductRepository
{
    public function updateStock(Product $product, int $quantity): void
    {
        $product->decrement('stock', $quantity);
    }
}

Создание отдельного класса для отправки уведомлений:

namespace App\Notifications;

use App\Models\Order;
use App\Models\User;
use App\Mail\OrderConfirmation;
use Illuminate\Support\Facades\Mail;

class OrderNotificationService
{
    public function sendConfirmation(Order $order): void
    {
        $user = User::find($order->user_id);
        Mail::to($user->email)->send(new OrderConfirmation($order));
    }
}

Преимущества подхода с SRP:

  1. Упрощение тестирования - каждый класс можно тестировать изолированно
  2. Повторное использование кода - сервисы можно использовать в разных частях приложения
  3. Упрощение поддержки - изменения в одной области не затрагивают другие
  4. Улучшение читаемости - каждый класс имеет четкое назначение
  5. Соблюдение принципов SOLID - SRP является первым принципом SOLID

В Laravel принцип единственной ответственности естественным образом вписывается в архитектуру приложения через использование сервис-контейнера, внедрение зависимостей и различные архитектурные паттерны, что делает код более поддерживаемым и масштабируемым.

Приведи пример единственной ответственности в Laravel | PrepBro