Приведи пример единственной ответственности в Laravel
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Пример принципа единственной ответственности (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:
- Упрощение тестирования - каждый класс можно тестировать изолированно
- Повторное использование кода - сервисы можно использовать в разных частях приложения
- Упрощение поддержки - изменения в одной области не затрагивают другие
- Улучшение читаемости - каждый класс имеет четкое назначение
- Соблюдение принципов SOLID - SRP является первым принципом SOLID
В Laravel принцип единственной ответственности естественным образом вписывается в архитектуру приложения через использование сервис-контейнера, внедрение зависимостей и различные архитектурные паттерны, что делает код более поддерживаемым и масштабируемым.