我已经创建了一个似乎不起作用的自定义表单验证器。它在表单提交期间调用,并在验证失败时返回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
断言, 会成功触发并失败,因此 在该字段上查找验证注释
我错过了什么?
答案 0 :(得分:2)
问题是validate()
的{{1}}方法不应该只返回true或false,它应该像这样构建违规行为:
ConstraintValidator
感谢我的同事抓住它。一切都按预期进行。