首页 > 解决方案 > 由于存储库中的意外实体更改时间,Symfony 表单自定义约束评估失败

问题描述

在 Symfony 3.3.17 应用程序中,我有一个带有使用自定义验证约束的参数的实体

class JobAd
{
    ...

    /**
     * @ORM\Column(type="string", length=2048)
     * @JobAdAssert\JobAdUrlExists
     */
    protected $url = '';

    ...
}

约束 JobAdUrlExists 看起来像这样

/**
 * @Annotation
 */
class JobAdUrlExists extends Constraint
{
    public $message = 'Cant use URL "{{ string }}". Its in use already.';

    public function validatedBy()
    {
        return 'job_add_url_exists.validator';
    }
}

这是 JobAdUrlExistsValidator

class JobAdUrlExistsValidator extends ConstraintValidator
{
    // ...

    public function validate( $value, Constraint $constraint )
    {
        // ...

        if( /* logic here */ ) {
            $this->context->buildViolation( $constraint->message )
                ->setParameter( '{{ string }}', $value )
                ->addViolation();
        }
    }
}

表单类型定义为

class JobAdType extends AbstractType implements ContainerAwareInterface
{
    use ContainerAwareTrait;

    public function buildForm( FormBuilderInterface $builder, array $options )
    {
        // ...

        $builder->add( 'url', TextType::class, array(
            'label' => $translator->trans( 'URL' ),
            'required' => false,
            'translation_domain' => 'messages',
            'mapped' => true,
            'data' => $url
        ));

        // add other form fields ...
    }

    public function configureOptions( OptionsResolver $resolver )
    {
        $resolver->setDefaults(
            array(
                'data_class' => 'AppBundle\Entity\JobAd',
            )
        );
    }

    public function getBlockPrefix()
    {
        return 'job_ad';
    }
}

我的应用程序逻辑创建一个这样的视图

// ...

$jobAd = $jobAdRepository->findOneBy( array( 'url' => $job_ad_url ) );
$jobAdForm = $this->createForm( JobAdType::class, $jobAd );

if('POST' === $request->getMethod()) {
    if( $request->request->has( 'job_ad' ) ) {
        $jobAdForm->handleRequest($request);
        if( $jobAdForm->isSubmitted() && $jobAdForm->isValid() ) {
            // ...
            $em->persist( $jobAd );
            $em->flush();
        }
    }
}

return $this->render( $jobAdTemplate, array(
    'form_job_ad' => $jobAdForm->createView(),
));

最后 - 树枝视图输出这样的形式

{{ form_start(form_job_ad) }}
{{ form_errors(form_job_ad) }}
{{ form_widget(form_job_ad.url) }}
{{ form_end(form_job_ad) }}

现在 - 当我在表单字段中插入一个触发验证错误的 URL 时,会发生三件事让我很困惑:

  1. JobAdUrlExistsValidator 中的 validate 方法构建违规。但是,在评估 URL 是否存在时,我获取了现有的 JobAd,令人惊讶的是,代表现在与表单一起呈现的 JobAd 的 URL 参数已经设置为新的(并且可能是错误的)URL。这是否意味着在验证实际发生之前更改了用于填写表单的基本实体?
  2. 尽管对 $jobAdForm->isValid() 的调用没有返回 true,因此 $em->persist() 和 $em->flush() 都没有被调用,但前端的表单不会输出任何错误并显示新的 URL作为其预设值。然而,代表实体的 DB 条目不会改变 - 因此当再次加载页面时,URL 又是旧的。
  3. 当输入一个在被约束检查时不应该失败的 URL 时,验证也会说这也是一个错误。当记住 1 中的观察时,这并不奇怪。)-> 实体的 URL 参数在实际检查任何内容之前已更改,因此在检查新 url 是否已经存在时,它显然会失败,因为它确实存在 - 在条目中它应该是现在插入。

我现在很困惑,有点绝望,不知道在哪里寻找问题的解决方案......非常欢迎任何提示:-)

标签: formssymfonyconstraintssymfony-3.3symfony3.x

解决方案


推荐阅读