TL; DR:未根据设置验证规则设置必需属性。
validation_groups是一种用于定义表单中应该验证的内容(以及如何)的一种比较方式。 我有这个经验与#34; Register"和"更新个人资料"形式。
我不能工作的是一个小的UI故障。在所有"必需"字段,此字段应标有*****。
可根据验证规则猜测所需选项(即 是字段NotBlank或NotNull)或Doctrine元数据(即 可空的领域)。作为您的客户端,这非常有用 验证将自动匹配您的验证规则。
这似乎不起作用,我当然可以覆盖所需的内容,如果我将其设置为false
,则按预期不会显示。
但是,如果我将validate_group用于profile_update,则密码字段不在validation_group中 - 如果为空,则不会将其标记为失败元素。但required
属性仍然设置。
所以提出问题 - required
标志如何基于实体的@Assert
注释?
正如您在图片上看到的那样,密码字段标记为"必需"但是,按照预期,未经过验证。同样,这不是验证问题,它只是具有必需属性的UI问题。
不要认为它会有多大帮助,但这里是相关的(短端)代码部分:
实体\用户:
class User implements UserInterface
{
use Timestampable;
use Blameable;
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(type="string", unique=true, length=200, nullable=false)
* @Assert\NotBlank(groups={"default"})
* @Assert\Email(groups={"default"})
* @Assert\Length(max = "200", groups={"default"})
*/
private $email;
/**
* @ORM\Column(type="string", length=64, nullable=false)
* @Assert\NotBlank(groups={"create"})
* @RollerworksPassword\PasswordStrength(minLength=6, minStrength=2)
*/
private $password;
[....]
}
表单\用户类型:
class UserType extends AbstractType
{
[...]
/**
* @param FormBuilderInterface $builder
* @param array $options
*
* @return misc
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstname', 'text', array('label' => 'Firstname'))
->add('lastname', 'text', array('label' => 'Lastname'))
->add('email', 'email', array('label' => 'EMail'))
->add('password', 'repeated', [
'type' => 'password',
'label' => 'Password',
'invalid_message' => 'Password fields must match',
'first_options' => ['label' => 'Password'],
'second_options' => ['label' => 'Repeat Password']
]
);
[...]
$builder
->add('save', 'submit', array('label' => 'Save'));
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => function(FormInterface $form) {
$data = $form->getData();
if ($data->getId() == null) {
return array('default', 'create');
}
return array('default');
},
'data_class' => 'Dpanel\Model\Entity\User',
));
}
[...]
}
视图\ form.html.twig
[...]
{{ form(form, {'style': 'horizontal', 'col_size': 'xs', 'align_with_widget': true, 'attr': {'novalidate': 'novalidate'}}) }}
[...]
答案 0 :(得分:2)
所以,在没有找到为什么这不起作用之后,我决定自己编写功能。
为了使这项工作在this Article和JsFormValidatorBundle
的来源中找到了很大的帮助我做的是: 使用FormType扩展调用服务类来获取实体的约束。 一旦我知道哪些字段元素不需要,我就修改视图并相应地设置所需的变量。
警告强> 此代码无法进行可扩展测试,可能无法在您的配置中使用!
<强>表单\扩展\ AutoRequireExtension.php:强>
<?php
namespace Cwd\GenericBundle\Form\Extension;
use Cwd\GenericBundle\Form\Subscriber\AutoRequire as AutoRequireSubscriber;
use Cwd\GenericBundle\Form\Service\AutoRequire as AutoRequireService;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
use JMS\DiExtraBundle\Annotation as DI;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
/**
* Class AutoRequireExtension
*
* @package Cwd\GenericBundle\Form\Extension
* @DI\Service("cwd.generic.form.extension.autorequire")
* @DI\Tag("form.type_extension", attributes={ "alias"="form" })
*/
class AutoRequireExtension extends AbstractTypeExtension
{
/**
* @var AutoRequireService
*/
protected $service;
/**
* @var bool
*/
protected $enabled;
/**
* @param AutoRequireService $service
* @param bool $enabled
*
* @DI\InjectParams({
* "service" = @DI\Inject("cwd.generic.form.service.autorequire"),
* "enabled" = @DI\Inject("%cwd.genericbundle.form.extension.autorequire.enabled%")
* })
*/
public function __construct(AutoRequireService $service, $enabled = false)
{
$this->service = $service;
$this->enabled = $enabled;
}
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ($this->enabled) {
$builder->addEventSubscriber(new AutoRequireSubscriber($this->service));
}
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
if ($this->enabled) {
if (isset($this->service->fields[$view->vars['name']])) {
$view->vars['required'] = $this->service->fields[$view->vars['name']];
}
// Password Repeat Fallback
if ($view->vars['name'] == 'first' || $view->vars['name'] == 'second') {
$view->vars['required'] = $this->service->fields['password'];
}
}
}
/**
* Returns the name of the type being extended.
*
* @return string The name of the type being extended
*/
public function getExtendedType()
{
return 'form';
}
}
<强>表单\订户\ AutoRequire.php:强>
<?php
namespace Cwd\GenericBundle\Form\Subscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Cwd\GenericBundle\Form\Service\AutoRequire as AutoRequireService;
/**
* Class AutoRequire
*
* @package Cwd\GenericBundle\Form\Subscriber
*/
class AutoRequire implements EventSubscriberInterface
{
protected $service = null;
/**
* @param AutoRequireService $service
*/
public function __construct(AutoRequireService $service)
{
$this->service = $service;
}
/**
* @return array
*/
public static function getSubscribedEvents()
{
return array(FormEvents::PRE_SUBMIT => array('onFormSetData', -10));
}
/**
* @param FormEvent $event
*/
public function onFormSetData(FormEvent $event)
{
/** @var Form $form */
$form = $event->getForm();
$this->service->process($this->getParent($form));
}
/**
* @param Form|FormInterface $element
*
* @return \Symfony\Component\Form\Form
*/
protected function getParent($element)
{
if (!$element->getParent()) {
return $element;
} else {
return $this->getParent($element->getParent());
}
}
}
<强>表单\服务\ AutoRequire.php:强>
namespace Cwd\GenericBundle\Form\Service;
use JMS\DiExtraBundle\Annotation as DI;
use Symfony\Component\Form\Form;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Class AutoRequire
*
* @DI\Service("cwd.generic.form.service.autorequire")
*/
class AutoRequire
{
/**
* @var ValidatorInterface
*/
protected $validator;
public $fields = array();
protected $groups = null;
/**
* @param ValidatorInterface $validator
*
* @DI\InjectParams({
* "validator" = @DI\Inject("validator")
* })
*/
public function __construct(ValidatorInterface $validator)
{
$this->validator = $validator;
}
/**
* Add a new form to processing queue
*
* @param \Symfony\Component\Form\Form $form
*
* @return array
*/
public function process(Form $form)
{
// no need to run for every field
if ($this->groups === null) {
$this->groups = $this->getValidationGroups($form);
}
// no need to run for every field
if (count($this->fields) == 0) {
$this->fields = $this->getValidations($form, $this->groups);
}
}
/**
* Get validation groups for the specified form
*
* @param Form|FormInterface $form
*
* @return array|string
*/
protected function getValidationGroups(Form $form)
{
$result = array('Default');
$groups = $form->getConfig()->getOption('validation_groups');
if (empty($groups)) {
// Try to get groups from a parent
if ($form->getParent()) {
$result = $this->getValidationGroups($form->getParent());
}
} elseif (is_array($groups)) {
// If groups is an array - return groups as is
$result = $groups;
} elseif ($groups instanceof \Closure) {
$result = call_user_func($groups, $form);
}
return $result;
}
private function getValidations(Form $form, $groups)
{
$fields = array();
$parent = $form->getParent();
if ($parent && null !== $parent->getConfig()->getDataClass()) {
$fields += $this->getConstraints($parent->getConfig()->getDataClass(), $groups);
}
if (null !== $form->getConfig()->getDataClass()) {
$fields += $this->getConstraints($form->getConfig()->getDataClass(), $groups);
}
return $fields;
}
protected function getConstraints($obj, $groups)
{
$metadata = $this->validator->getMetadataFor($obj);
$fields = array();
foreach ($metadata->members as $elementName => $d) {
$fields[$elementName] = false;
$data = $d[0];
foreach ($data->constraintsByGroup as $group => $constraints) {
if (in_array($group, $groups) && count($constraints) > 0) {
$fields[$elementName] = true;
break;
}
}
}
return $fields;
}
/**
* Gets metadata from system using the entity class name
*
* @param string $className
*
* @return ClassMetadata
* @codeCoverageIgnore
*/
protected function getMetadataFor($className)
{
return $this->validator->getMetadataFactory()->getMetadataFor($className);
}
/**
* Generate an Id for the element by merging the current element name
* with all the parents names
*
* @param Form $form
*
* @return string
*/
protected function getElementId(Form $form)
{
/** @var Form $parent */
$parent = $form->getParent();
if (null !== $parent) {
return $this->getElementId($parent) . '_' . $form->getName();
} else {
return $form->getName();
}
}
}
可以在https://gitlab.cwd.at/symfony/cwdgenericbundle/tree/master/Form
上找到Up2Date版本答案 1 :(得分:0)
我必须承认我没有花时间详细介绍Rufinus’ own answer。但是,这是一个更简单的解决方案,特别是如果您只需要一个字段使用该解决方案:
// UserType.php
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
// ...
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$form = $event->getForm();
if (in_array('create', $form->getConfig()->getOption('validation_groups'))) {
$form->add('password');
}
else {
$form->add('password', null, 'required'=>false);
}
});
想法是使用Event Listener来确定是否设置了create
验证组,然后添加带有或不带有'required'=>false
的密码字段。