Symfony自定义验证不起作用

时间:2015-07-16 21:47:53

标签: 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);
    }
}

由于User而对data_type实体进行了验证。以下是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.
}

最后,这是我用来检查的控制器方法。我会忽略视图,因为它可能无关紧要。 (我将我的控制器定义为服务,因此没有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断言, 会成功触发并失败,因此 在该字段上查找验证注释

我错过了什么?

1 个答案:

答案 0 :(得分:2)

问题是validate()的{​​{1}}方法不应该只返回true或false,它应该像这样构建违规行为:

ConstraintValidator

感谢我的同事抓住它。一切都按预期进行。