Любите загадки? Событие еще доступно на сайте.
Inn_100_gramm

Если вы видите это, значит, я еще не придумал, что написать.

Паттерн Конвейер (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 позволяет легко и элегантно управлять сложными запросами. Это улучшает читаемость кода и упрощает его поддержку, предоставляя более гибкий и модульный способ обработки запросов.

0
Inn_100_gramm

Если вы видите это, значит, я еще не придумал, что написать.

Динамическое подключение трейтов в Laravel: возможно ли это?

В PHP и Laravel трейты являются мощным инструментом для повторного использования кода и улучшения его структуры. Трейты позволяют включать методы в различные классы, избегая дублирования кода и улучшая его читаемость. Однако, возникает вопрос: можно ли динамически подключать трейты к уже существующим объектам в Laravel? В этой статье мы рассмотрим возможности и альтернативные подходы для достижения подобной функциональности.

Почему может понадобиться динамическое подключение трейтов?

Динамическое подключение трейтов может быть полезно в следующих случаях:

Гибкость кода: Возможность изменять поведение объектов в зависимости от контекста или условий. Повторное использование кода: Уменьшение дублирования кода и улучшение структуры приложения. Упрощение тестирования: Легкость замены или изменения функционала для тестирования различных сценариев.

Технические ограничения

Трейты в PHP предназначены для композиции кода на этапе компиляции. Это означает, что они подключаются к классам во время компиляции и не могут быть изменены или добавлены к объектам динамически во время выполнения программы. Тем не менее, есть альтернативные подходы для достижения схожей функциональности.

Альтернативные подходы

Делегирование Делегирование позволяет одному объекту передавать вызовы методов другому объекту. Это может быть достигнуто через композицию и использование интерфейсов.

Шаг 1: Создание трейта

trait LoggableTrait
{
    public function log(string $message): void
    {
        // Логика для логирования
        echo "Log: " . $message;
    }
}

Шаг 2: Создание интерфейса

interface LoggableInterface
{
    public function log(string $message): void;
}

Шаг 3: Реализация интерфейса в классе

class Logger implements LoggableInterface
{
    use LoggableTrait;
}

Шаг 4: Использование композиции

class User
{
    protected LoggableInterface $logger;

    public function __construct(
        protected object $logger
    ) {
    }

    public function performAction(string $action): void
    {
        $this->logger->log("Performing action: " . $action);
        // Другая логика
    }
}

// Пример использования
$logger = new Logger();
$user = new User($logger);
$user->performAction('Login');

Магические методы

Магические методы, такие как __call, позволяют перехватывать вызовы методов и перенаправлять их на другой объект. Этот подход может быть полезен для делегирования вызовов.

Пример реализации с магическим методом __call

class User
{
    public function __construct(
        protected object $logger
    ) {
    }

    public function __call(string $method, array $args)
    {
        if (method_exists($this->logger, $method)) {
            return call_user_func_array([$this->logger, $method], $args);
        }
        
        throw new BadMethodCallException("Method {$method} does not exist.");
    }
}

// Пример использования
$logger = new Logger();
$user = new User($logger);
$user->log('Login');

Преимущества альтернативных подходов

  • Гибкость: Возможность динамически изменять поведение объектов, что упрощает адаптацию к различным условиям и контекстам.
  • Повторное использование кода: Уменьшение дублирования кода и улучшение структуры приложения через инъекцию зависимостей.
  • Легкость тестирования: Упрощение замены или изменения функционала для тестирования различных сценариев.

Заключение

В PHP и Laravel трейты являются мощным инструментом для улучшения структуры и повторного использования кода. Однако динамическое подключение трейтов невозможно из-за ограничений языка. Вместо этого можно использовать делегирование, магические методы и композицию для достижения схожей функциональности. Эти подходы позволяют добавлять и изменять поведение объектов динамически, улучшая гибкость и тестируемость кода.

0