Паттерн Конвейер (Pipeline Pattern) в Laravel
Пайплайны предоставляют элегантное решение для последовательного применения множества фильтров к запросу Eloquent. В Laravel пайплайны позволяют инкапсулировать логику фильтрации в отдельные классы и затем последовательно применять их к запросу. Это повышает читаемость, тестируемость и расширяемость кода.
Давайте рассмотрим, как можно использовать пайплайны для реализации фильтров в Eloquent:
Шаг 1: Создание фильтров
Каждый фильтр будет отдельным классом, реализующим интерфейс FilterInterface.
<?php
namespace App\Pipelines\Filters;
use Illuminate\Database\Eloquent\Builder;
interface FilterInterface
{
public static function apply(Builder $builder, $value): Builder;
}
Примеры фильтров:
Фильтр по названию:
<?php
namespace App\Pipelines\Filters;
use Illuminate\Database\Eloquent\Builder;
class NameFilter implements FilterInterface
{
public static function apply(Builder $builder, $value): Builder
{
return $builder->where('name', 'like', '%' . $value . '%');
}
}
Фильтр по категории:
<?php
namespace App\Pipelines\Filters;
use Illuminate\Database\Eloquent\Builder;
class CategoryFilter implements FilterInterface
{
public static function apply(Builder $builder, $value): Builder
{
return $builder->where('category', $value);
}
}
Фильтр по цене:
<?php
namespace App\Pipelines\Filters;
use Illuminate\Database\Eloquent\Builder;
class PriceFilter implements FilterInterface
{
public static function apply(Builder $builder, $value): Builder
{
return $builder->whereBetween('price', [$value['min'], $value['max']]);
}
}
Шаг 2: Создание класса пайплайна
Создайте класс ProductPipeline, который будет последовательно применять фильтры.
<?php
namespace App\Pipelines;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Pipeline;
class ProductPipeline
{
protected array $filters = [];
public function __construct(array $filters)
{
$this->filters = $filters;
}
public function apply(Builder $builder): Builder
{
return app(Pipeline::class)
->send($builder)
->through($this->filters)
->thenReturn();
}
}
Шаг 3: Применение пайплайнов в контроллере
Используйте пайплайн в контроллере для фильтрации данных.
<?php
namespace App\Http\Controllers;
use App\Models\Product;
use App\Pipelines\Filters\NameFilter;
use App\Pipelines\Filters\CategoryFilter;
use App\Pipelines\Filters\PriceFilter;
use App\Pipelines\ProductPipeline;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
/**
* Class ProductController
*/
class ProductController extends Controller
{
/**
* Получение списка продуктов с фильтрацией
*
* @param Request $request
* @return JsonResponse
*/
public function index(Request $request): JsonResponse
{
$pipeline = new ProductPipeline([
NameFilter::class,
CategoryFilter::class,
PriceFilter::class,
]);
$products = $pipeline->apply(Product::query())->get();
return response()->json($products);
}
}
Шаг 4: Валидация входящих данных
Создайте FormRequest для валидации входящих данных.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
/**
* Class ProductFilterRequest
*/
class ProductFilterRequest extends FormRequest
{
/**
* Правила валидации
*
* @return array
*/
public function rules(): array
{
return [
'name' => 'nullable|string|max:255',
'category' => 'nullable|string|max:255',
'price' => 'nullable|array',
'price.min'=> 'nullable|numeric|min:0',
'price.max'=> 'nullable|numeric|min:0',
];
}
}
Заключение
Использование пайплайнов для фильтрации данных в Eloquent позволяет легко и элегантно управлять сложными запросами. Это улучшает читаемость кода и упрощает его поддержку, предоставляя более гибкий и модульный способ обработки запросов.