PHP项目Symfony form与算术验证

wen PHP项目 2

本文目录导读:

PHP项目Symfony form与算术验证

  1. 基础算术验证方法
  2. 使用示例

我来详细介绍 Symfony Form 中的算术验证方法。

基础算术验证方法

使用内置约束

use Symfony\Component\Validator\Constraints as Assert;
class Product 
{
    #[Assert\Range(
        min: 10,
        max: 100,
        notInRangeMessage: '数值必须在 {{ min }} 到 {{ max }} 之间',
    )]
    private float $price;
    #[Assert\Positive(message: '数值必须为正数')]
    #[Assert\LessThan(value: 1000)]
    private float $quantity;
}

自定义算术验证器

// src/Validator/ArithmeticValidator.php
namespace App\Validator;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
class ArithmeticValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint)
    {
        if (!$constraint instanceof Arithmetic) {
            throw new UnexpectedTypeException($constraint, Arithmetic::class);
        }
        if (null === $value || '' === $value) {
            return;
        }
        // 自定义算术验证逻辑
        if ($value < $constraint->min || $value > $constraint->max) {
            $this->context->buildViolation($constraint->message)
                ->setParameter('{{ value }}', $value)
                ->setParameter('{{ min }}', $constraint->min)
                ->setParameter('{{ max }}', $constraint->max)
                ->addViolation();
        }
    }
}
// src/Validator/Arithmetic.php
namespace App\Validator;
use Symfony\Component\Validator\Constraint;
#[Attribute]
class Arithmetic extends Constraint
{
    public string $message = '数值 {{ value }} 必须在 {{ min }} 到 {{ max }} 之间';
    public float $min = 0;
    public float $max = 100;
    public function __construct(
        ?float $min = null, 
        ?float $max = null, 
        ?string $message = null, 
        ?array $groups = null, 
        $payload = null
    ) {
        parent::__construct($groups, $payload);
        $this->min = $min ?? $this->min;
        $this->max = $max ?? $this->max;
        $this->message = $message ?? $this->message;
    }
}

复杂算术验证示例

// src/Form/Type/OrderType.php
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints as Assert;
class OrderType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('quantity', NumberType::class, [
                'label' => '数量',
                'constraints' => [
                    new Assert\NotBlank(),
                    new Assert\Type(['type' => 'numeric', 'message' => '请输入有效的数字']),
                ]
            ])
            ->add('unitPrice', NumberType::class, [
                'label' => '单价',
                'constraints' => [
                    new Assert\NotBlank(),
                    new Assert\Positive(),
                ]
            ])
            ->add('discount', NumberType::class, [
                'label' => '折扣率',
                'constraints' => [
                    new Assert\Range([
                        'min' => 0,
                        'max' => 100,
                        'notInRangeMessage' => '折扣率必须在 {{ min }}% 到 {{ max }}% 之间',
                    ])
                ]
            ])
            ->add('submit', SubmitType::class, ['label' => '提交订单']);
        // 添加事件监听器进行复杂验证
        $builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
            $data = $event->getData();
            $form = $event->getForm();
            // 计算总价
            $totalPrice = $data->getQuantity() * $data->getUnitPrice() * (1 - $data->getDiscount() / 100);
            // 验证总价
            if ($totalPrice < 0) {
                $form->get('quantity')->addError(new FormError('订单总价不能为负数'));
            }
            if ($totalPrice > 10000) {
                $form->addError(new FormError('订单总价不能超过 10,000 元'));
            }
            // 验证折扣合理性
            if ($data->getDiscount() > 50 && $data->getQuantity() < 10) {
                $form->get('discount')->addError(new FormError('小额订单折扣不能超过 50%'));
            }
        });
    }
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Order::class,
        ]);
    }
}

使用DTO进行高级验证

// src/DTO/ArithmeticDTO.php
namespace App\DTO;
use Symfony\Component\Validator\Constraints as Assert;
class ArithmeticDTO
{
    #[Assert\NotBlank]
    #[Assert\Type('float')]
    private float $value;
    #[Assert\NotBlank]
    #[Assert\Type('float')]
    #[Assert\GreaterThanOrEqual(0)]
    private float $percentage;
    #[Assert\Callback]
    public function validateArithmetic(ExecutionContextInterface $context): void
    {
        // 检查百分比计算
        $calculatedValue = $this->value * ($this->percentage / 100);
        if ($calculatedValue > $this->value) {
            $context->buildViolation('计算结果不能大于原始值')
                ->atPath('percentage')
                ->addViolation();
        }
        // 检查结果是否合理
        if ($calculatedValue < 0) {
            $context->buildViolation('计算结果不能为负数')
                ->atPath('value')
                ->addViolation();
        }
    }
}

在表单中使用Callback验证

// src/Form/Type/CalculationType.php
namespace App\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
class CalculationType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('x', NumberType::class)
            ->add('y', NumberType::class, [
                'constraints' => [
                    new Callback(function ($y, ExecutionContextInterface $context) {
                        $form = $context->getRoot();
                        $x = $form->get('x')->getData();
                        // 验证除法
                        if ($y !== null && $y == 0 && $x !== null && $x != 0) {
                            $context->buildViolation('y 不能为零当 x 不为零时')
                                ->addViolation();
                        }
                        // 验证百分比
                        if ($x !== null && $y !== null && $y > $x * 2) {
                            $context->buildViolation('y 不能超过 x 的两倍')
                                ->addViolation();
                        }
                    })
                ]
            ]);
    }
}

百分比验证示例

// src/Validator/PercentageValidator.php
namespace App\Validator;
class PercentageValidator extends ConstraintValidator
{
    public function validate($value, Constraint $constraint)
    {
        if (null === $value || '' === $value) {
            return;
        }
        // 限制百分比范围
        if ($value < 0 || $value > 100) {
            $this->context->buildViolation('百分比必须在 0 到 100 之间')
                ->setParameter('{{ value }}', $value)
                ->addViolation();
        }
        // 检查精度
        $decimalPlaces = strlen(substr(strrchr($value, '.'), 1));
        if ($decimalPlaces > 2) {
            $this->context->buildViolation('百分比最多保留两位小数')
                ->addViolation();
        }
    }
}

使用示例

// 在控制器中使用
public function createOrder(Request $request): Response
{
    $order = new Order();
    $form = $this->createForm(OrderType::class, $order);
    $form->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {
        // 订单验证通过
        $this->entityManager->persist($order);
        $this->entityManager->flush();
        return $this->redirectToRoute('order_success');
    }
    return $this->render('order/create.html.twig', [
        'form' => $form->createView(),
    ]);
}

这些方法可以帮助你实现各种算术验证需求,从简单的范围验证到复杂的条件计算验证。

抱歉,评论功能暂时关闭!