PHP - Laravel - Лучший способ управления условными правилами - PullRequest
2 голосов
/ 06 февраля 2020

У меня есть приложение, использующее Laravel framework, и есть некоторые условные правила, которые я не знаю, как лучше всего кодировать и поддерживать.

Вариант использования: условно применить промо-код

  • промо-код может быть применен в пределах указанного c даты или диапазона дат
  • промо-код может быть применен при заказе> = $ 100
  • промо-код может применяться для уточнения c item
  • ...

Основой c является решение написать несколько операторов IF ELSE для проверки 1 на 1. Например:

if ($promo->specific_date) {

} 
elseif ($promo->date_range >= 'date' && $promo->specific_date <= 'date') {

}

if ($totalAmount < 100) {
    // Dont allow
}

if (! $promo->allowed_items) {
    // Dont allow
}

// More conditions ...

I Я вижу, что код будет проблематичным c в тестировании и обслуживании.

Так что мне интересно, есть ли лучший способ справиться с этим? Например, используя OOP способ?

P / S: Чтобы уточнить мой случай использования:

  • Мне нужно пройти все правила, чтобы сделать акцию действительной
  • Я думаю о создании модели правил, чтобы у меня был CRUD для управления ими, а в бэкэнде я могу выполнить запрос, чтобы получить все правила, затем вызвать класс для конвейера и проверить все правила. .. (не уверен, хорошо это или плохо)

Спасибо,

Ответы [ 3 ]

0 голосов
/ 06 февраля 2020

Вы можете определить свойства продвижения в вашей модели Promotion (что означает, что они, вероятно, должны храниться где-то в вашей базе данных), а затем иметь нормализованный валидатор, который можно вызвать для любого промо.

Вот пример / псевдокод для объяснения процесса (это PHP 7.4, просто удалите типы свойств, чтобы он работал в предыдущих версиях):

final class Promotion
{
  private DateTime $minDate;
  private DateTime $maxDate;
  private DateTime $minAmount;
  private array $allowedItems;

  public function getMinDate(): DateTime
  {
    return $this->minDate;
  }

  public function getMaxDate(): DateTime
  {
    return $this->maxDate;
  }

  public function getMinAmount(): DateTime
  {
    return $this->minAmount;
  }

  public function getAllowedItems(): array
  {
    return $this->allowedItems;
  }
}

final class PromotionValidator
{
  public function isPromotionValid(Promotion $promo, array $purchasedItems, int $totalAmount): bool
  {
    $now = new \DateTime();

    if ($now < $promo->getMinDate() || $now > $promo->getMaxDate()) {
      return false;
    }

    if ($totalAmount < $promo->getMinAmount()) {
      return false;
    }

    if (count(array_intersect($purchasedItems, $promo->getAllowedItems())) !== count($promo->getAllowedItems())) {
      return false;
    }

    return true;
  }
}

Использование:

$promotionValidator = new PromotionValidator();
$promoIsValid = $promotionValidator->isPromotionValid($promo, $cartItems, $cartAmount);
0 голосов
/ 06 февраля 2020

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

Представьте себе, что вы можете правильно извлечь конфигурацию ограничений из базы данных и создать массив (или что-то вроде экземпляра ConstraintBag). ), который содержит все ограничения, которые необходимо проверить:

configuration
$constraints = [
    DateRangeConstraint::class,
    TotalAmountConstraint::class,
    AllowedItemsContraint::class,
];

Каждое ограничение может соответствовать одному и тому же интерфейсу (Constraint в этом Po C), который будет определять единственный метод handle:

use Closure;

class DateRangeConstraint implements Constraint
{
    public function handle($order, Closure $next)
    {
        if ($order->promo->start_date >= 'date' || $order->promo->end_date <= 'date') {
            throw new PromotionConstraintException($this);
        }

        return $next($order);
    }
}

Тогда в вашем методе контроллера / сервиса вы можете использовать этот массив правил в конвейере и передать объект заказа (или объект, который содержит все части, необходимые для проверки всех ограничений), хотя ограничения. Если что-то из этого не получится, вы можете вызвать пользовательское исключение (возможно, одно для каждой категории ограничения / одно для ограничения) и вернуть итоговый результат процесса validation:

// Do not forget to add the use statement
use Illuminate\Pipeline\Pipeline;

class PromotionValidationService
{
    protected $constraints;

    // Pass in the constraints array you have already built
    public function __construct($constraints)
    {
        $this->constraints = $constraints;
    }

    // Start the validation process and cycle through all the constraints
    // I would pass in the order object as you might need to access the total
    // order amount and/or the items in the order
    public function validate($order)
    {
        try {
            app(Pipeline::class)
                ->send($order)
                ->through($this->constraints);
        } catch (PromotionConstraintException $exception) {
            // Handle the exception and return false or rethrow
            // the exception for further handling from the caller.
            return false;
        }

        return true;
    }
}

Очевидно, что это все еще подтверждение концепций и потребует больше изучения и архитектурного планирования для обработки различных ограничений, которые вам, возможно, потребуется проверить (например, прохождение всего объекта $order может быть не лучшей идеей, или оно может быть еще недоступно при проверке продвижения ограничения). Однако это может быть гибкой альтернативой фиксированной последовательности if / elses, которую необходимо редактировать для каждого изменения.

0 голосов
/ 06 февраля 2020

используйте switch оператор здесь, потому что это быстрее, чем , если

по умолчанию установлено promo_code flag false, потому что теперь требуется только мое любое условие: true, затем установить promo_code flag true ..

если вы хотите использовать оператор if один за другим, то это хорошо, потому что легко читается и поддерживается

$promo_code_flag = false;

if ($promo->specific_date) {
    $promo_code_flag = true;
} 
elseif ($promo->date_range >= 'date' && $promo->specific_date <= 'date') {
     $promo_code_flag = true;
}

if ($totalAmount > 100) {
    $promo_code_flag = true;
}

if ( $promo->allowed_items) {
    $promo_code_flag = true;
}

if($promo_code_flag) { 
 //allow promo code
}//other wise it will not allowe
...