Laravel Pipelines – это малоизвестная, но мощная возможность Laravel, которая не описана в официальной документации. Тем не менее, этот инструмент широко используется внутри фреймворка для выполнения последовательных операций. В этой статье мы рассмотрим, как Laravel Pipelines может быть полезен в разработке CRM-систем, а также приведем примеры использования и полезные ссылки.
В каких задачах Laravel Pipelines даст выгоды
Представим, что у нас есть некоторые исходные данные, с которыми нужно произвести последовательно ряд действий. Например, добавление нового контакта в CRM-систему.
Пример 1: Добавление нового контакта
Когда пользователь добавляет новый контакт, данные приходят в контроллер. Контакт проходит через ряд шагов перед добавлением в базу данных:
- Проверка наличия всех обязательных полей.
- Валидация данных: проверка корректности email, телефона и т.д.
- Удаление лишних пробелов и форматирование текста.
- Проверка уникальности контакта.
- Создание уникального идентификатора контакта.
- Сохранение контакта в базу данных.
Пример 2: Обработка запроса на поддержку
Когда пользователь создает запрос на поддержку, данные запроса также проходят через несколько шагов:
- Проверка наличия всех обязательных полей.
- Валидация данных: проверка корректности email, описания проблемы и т.д.
- Удаление лишних пробелов и форматирование текста.
- Создание уникального идентификатора запроса.
- Сохранение запроса в базу данных.
- Отправка уведомления пользователю.
- Назначение запроса на поддержку сотруднику.
Эволюция кода
Рассмотрим, как эволюционирует код по мере добавления новых шагов, соблюдая принципы чистой архитектуры и DDD.
Начальная стадия
Сначала у нас есть контроллер и сервис, который выполняет основную логику. Например, простое добавление контакта:
public function store(Request $request, ContactService $contactService)
{
$contact = $contactService->create($request->all());
return response()->json($contact, 201);
}
class ContactService
{
protected $contactRepository;
public function __construct(ContactRepository $contactRepository)
{
$this->contactRepository = $contactRepository;
}
public function create(array $data)
{
return $this->contactRepository->create($data);
}
}
class ContactRepository
{
public function create(array $data)
{
return Contact::create($data);
}
}
Добавление новых шагов
С добавлением новых шагов код становится более сложным. Мы выносим логику в отдельные классы:
public function store(Request $request, ContactService $contactService)
{
$contact = $contactService->create($request->all());
return response()->json($contact, 201);
}
class ContactService
{
protected $contactRepository;
public function __construct(ContactRepository $contactRepository)
{
$this->contactRepository = $contactRepository;
}
public function create(array $data)
{
$data = (new TrimWhitespace())->handle($data);
$data = (new ValidateUniqueEmail())->handle($data);
$data = (new GenerateUniqueId())->handle($data);
return $this->contactRepository->create($data);
}
}
Унификация интерфейсов
Мы можем унифицировать интерфейсы и использовать Laravel Pipelines для последовательного выполнения шагов:
public function store(Request $request, ContactService $contactService)
{
$contact = $contactService->create($request->all());
return response()->json($contact, 201);
}
class ContactService
{
protected $contactRepository;
public function __construct(ContactRepository $contactRepository)
{
$this->contactRepository = $contactRepository;
}
public function create(array $data)
{
$pipes = [
TrimWhitespace::class,
ValidateUniqueEmail::class,
GenerateUniqueId::class,
];
$data = app(Pipeline::class)
->send($data)
->through($pipes)
->then(function ($data) {
return $this->contactRepository->create($data);
});
return $data;
}
}
Пример использования Laravel Pipelines
Интерфейс Pipe
Каждый шаг (pipe) должен реализовывать интерфейс Pipe:
namespace App\Pipes;
use Closure;
interface Pipe
{
public function handle($data, Closure $next);
}
Пример шага (Pipe)
namespace App\Pipes;
use Closure;
class TrimWhitespace implements Pipe
{
public function handle($data, Closure $next)
{
$data['name'] = trim($data['name']);
return $next($data);
}
}
Сопутствующие проблемы
Иногда на каком-то шаге нужно прервать выполнение или почистить данные в базе данных от выполненных шагов. Пример решения таких проблем:
class ContactService
{
protected $contactRepository;
public function __construct(ContactRepository $contactRepository)
{
$this->contactRepository = $contactRepository;
}
public function create(array $data)
{
$pipes = [
TrimWhitespace::class,
ValidateUniqueEmail::class,
GenerateUniqueId::class,
];
try {
DB::beginTransaction();
$data = app(Pipeline::class)
->send($data)
->through($pipes)
->then(function ($data) {
return $this->contactRepository->create($data);
});
DB::commit();
return $data;
} catch (Exception $e) {
DB::rollback();
throw $e;
}
}
}
Здесь используется транзакция для обеспечения целостности данных. В случае ошибки транзакция откатывается (DB::rollback()), и возвращается сообщение об ошибке.
Заключение
Laravel Pipelines – это мощный инструмент для управления последовательными операциями. Он позволяет избежать спагетти-кода и сделать код более чистым и поддерживаемым. В статье мы рассмотрели примеры использования Laravel Pipelines для управления контактами и запросами на поддержку в CRM-системе, соблюдая принципы чистой архитектуры и DDD. Надеемся, что эта информация будет полезной для вас в разработке ваших проектов.