php - Symfony 自定义验证不起作用

标签 php forms validation symfony

我创建了一个自定义表单验证器,但它似乎不起作用。它在表单提交期间被调用,并在验证失败时返回 false。但它似乎并没有告诉表单验证失败。

这是表单验证器:(代码框滚动)

<?php
namespace Redacted\AppBundle\Validator\Constraints;

use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class RequiredIfPlainPasswordSetValidator extends ConstraintValidator
{
    /**
     * RequestStack instance.
     *
     * @var Symfony\Component\HttpFoundation\RequestStack
     */
    protected $requestStack;

    /**
     * dependency injection.
     *
     * @param Symfony\Component\HttpFoundation\RequestStack $requestStack
     */
    public function __construct(RequestStack $requestStack)
    {
        $this->requestStack = $requestStack;
    }

    /**
     * if the plain password has been set, the current password must not be
     * empty.
     *
     * @param string $currentPassword
     * @param Symfony\Component\Validator\Constraint $constraint
     *
     * @return bool
     */
    public function validate($currentPassword, Constraint $constraint)
    {
        $plainPassword = $this->requestStack->getCurrentRequest()->request
            ->get('security_settings')['plainPassword']['first'];
        if ($plainPassword && !$currentPassword) {
            return false;
        }

        return true;
    }
}

这是随之而来的约束:

<?php
namespace Redacted\AppBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class RequiredIfPlainPasswordSet extends Constraint
{

    /**
     * error message
     *
     * @var string
     */
    protected $message = 'Please enter your current password.';

    /**
     * the alias for the related validator in services.yml
     *
     * @return string
     */
    public function validatedBy()
    {
        return 'required_if_plain_password_set';
    }
}

以及app/config/services.yml的相关部分:

services:
  redacted.appbundle.required_if_plain_password_set:
    class: Redacted\AppBundle\Validator\Constraints\RequiredIfPlainPasswordSetValidator
    arguments: ["@request_stack"]
    tags:
      - { name: validator.constraint_validator, alias: required_if_plain_password_set }

我创建了一个自定义表单类型:(代码框滚动)

<?php
namespace Redacted\AppBundle\Form;

use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\AbstractType;

class SecuritySettingsFormType extends AbstractType
{
    /**
     * the name of the form type
     *
     * @return string
     */
    public function getName()
    {
        return 'security_settings';
    }

    /**
     * add fields to form
     *
     * @param Symfony\Component\Form\FormBuilderInterface $formBuilder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $formBuilder, array $options)
    {
        $formBuilder
            ->add('email')
            ->add('currentPassword', 'password')
            ->add('plainPassword', 'repeated', [
                'type' => 'password',
                'invalid_message' => 'Passwords must match.',
            ]);
    }

    /**
     * configureOptions.
     *
     * @param Symfony\Component\OptionsResolver\OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $options = [
            'data_class' => 'AppBundle\Entity\User',
            'validation_groups' => ['security_settings'],
        ];
        $resolver->setDefaults($options);
    }
}

根据data_typeUser实体进行验证。以下是 User 实体的相关部分:

<?php

namespace Redacted\AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Redacted\AppBundle\Validator\Constraints\RequiredIfPlainPasswordSet;


class User implements UserInterface, \Serializable
{
    // ...

    /**
     * @var string (default: null)
     *
     * @Assert\NotBlank(message="user.email_required", groups={"security_settings"})
     * @Assert\Email(message="security_settings.valid_email" groups={"security_settings"})
     * @ORM\Column(name="email", type="string", length=255, nullable=true, unique=true)
     */
    private $email = null;

    /**
     * current password - used for validation only
     *
     * @RequiredIfPlainPasswordSet(
     *     message="security_settings.current_password_required",
     *     groups={"security_settings"}
     * )
     * @var string
     */
    protected $currentPassword;

    // ... setters and getters for above, etc.
}

最后,这是我用来检查的 Controller 方法。我将忽略该 View ,因为它可能无关紧要。 (我将 Controller 定义为服务,因此没有 redirectToRoute() 等)

// in the controller class...

/**
 * Security settings page - email, password, etc.
 *
 * @Route("/security-settings", name="security_settings")
 * @Template()
 *
 * @param Symfony\Component\HttpFoundation\Request $request
 *
 * @return array|Symfony\Component\HttpFoundation\RedirectResponse
 */
public function securitySettingsAction(Request $request)
{
    $loggedInUser = $this->tokenStorage->getToken()->getUser();
    $form = $this->formFactory
        ->create(new SecuritySettingsFormType(), $loggedInUser);
    $form->handleRequest($request);
    if ($form->isValid()) {
        $user = $form->getData();

        // persist the user
        $this->saveUserSettings($user);

        // set a success message
        $this->setSecuritySettingsFlashSuccess($request);

        // redirect back
        $route = $this->router->generate('security_settings');

        return new RedirectResponse($route);
    }

    return ['form' => $form->createView()];
}

这个想法是,只有在输入新密码时才需要当前密码。尽管 validate() 函数被调用并在应有的时候返回 false,但表单的 isValid() 返回 true 并且正在保存。如果我将 @Assert\NotBlank(groups={"security_settings"}) 断言添加到 User::currentPassword 字段,它确实会触发并成功失败,因此它正在寻找该字段的验证注释。

我错过了什么?

最佳答案

问题是 ConstraintValidatorvalidate() 方法不应该只返回 true 或 false,它应该构建如下所示的违规:

/**
 * if the plain password has been set, the current password must not be
 * empty.
 *
 * @param string $currentPassword
 * @param Symfony\Component\Validator\Constraint $constraint
 */
public function validate($currentPassword, Constraint $constraint)
{
    $plainPassword = $this->requestStack->getCurrentRequest()->request
        ->get('security_settings')['plainPassword']['first'];
    if ($plainPassword && !$currentPassword) {
        $this->context->buildViolation($constraint->message)
            ->atPath('currentPassword')->addViolation();
    }
}

感谢我的同事捕获了它。现在一切都按预期进行。

关于php - Symfony 自定义验证不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31465087/

相关文章:

php - WooCommerce 如何检查产品是否有货

javascript - 使用 Javascript 调用从 MySQL DB 读取网站内容的 PHP 文件

forms - 从继承的输入字段中设计选择菜单的样式?

javascript - 不允许粘贴任何非字母数字字符

JavaScript "Live"表单验证

php - 阻止或清除 HTML/CSS 标签

php - 使用curl时发生SSL写入错误

jquery - 更改表单的操作属性

forms - Vue form select 在选择相同元素时发出事件

hibernate - 使用 Hibernate 验证创建组验证的步骤