【问题标题】:How to update an unique entity field with Symfony 4 form如何使用 Symfony 4 表单更新唯一实体字段
【发布时间】:2018-12-02 21:23:48
【问题描述】:

我正在更新我的用户实体。它是在其唯一电子邮件上定义的唯一实体。 不幸的是,在更新我的实体时,由于此电子邮件唯一验证规则,我触发了验证错误。

我一直在尝试将 $user 传递给表单,以确保它将其视为用户更新,但没有运气。

这是一个 Ajax 表单。

您知道如何解决这个问题吗?

用户实体

class User implements UserInterface, \Serializable
{
/**
 * @ORM\Id
 * @ORM\Column(type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string $email
 * @ORM\Column(type="string", length=254, nullable=false)
 * @Assert\Email()
 * @Assert\NotBlank
 * @AcmeAssert\UniqueEmail
 */
private $email;

/**
 * @ORM\Column(type="string", length=25, nullable=true)
 */
private $username;
// and so on

我的控制器:

/**
 * @Route("/profile", name="profile")
*/
public function profile()
{
    $user = $this->getUser();
    $formAccount = $this->updateUserAccountForm( $user );

    return $this->render('platform/user/profile.html.twig',
        array(
            'user' => $user,
            'form_account' => $formAccount->createView()
        )
    );
}

/**
 * @Route("/profile/updateAccount", name="updateUserAccount", methods={"POST"})
 */
public function updateUserAccount(Request $request, UserPasswordEncoderInterface $passwordEncode)
{
    if (!$request->isXmlHttpRequest()) {
        return new JsonResponse(array('message' => 'Forbidden'), 400);
    }

    // Build The Form
    $user = $this->getUser();

    $form = $this->updateUserAccountForm($user);
    $form->handleRequest($request);

    if ($form->isValid()) {
        $user_form = $form->getData();

        // Check if the password is = to DB
        $current_password = $passwordEncoder->encodePassword($user, $user_form->getPlainPassword());

        if($user->getPassword() != $current_password){
            return new JsonResponse(['error' => 'wrong password!']);
        }

        // Encode the password (We could also do this via Doctrine listener)
        $password = $passwordEncoder->encodePassword($user_form, $user_form->getPlainPassword());
        $user_form->setPassword($password);

        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->merge($user_form);
        $entityManager->flush();

        $em = $this->getDoctrine()->getManager();
        $em->persist($user_form);
//            $em->flush();

        return new JsonResponse(array('message' => 'Success!'), 200);
    }

    $errors = [];
    foreach ($form->getErrors(true, true) as $formError) {
        $errors[] = $formError->getMessage();
    }
    $errors['userid'] = $user->getId();
    $errors['user'] = $user->getUsername();
    return new JsonResponse($errors);
}

/**
 * Creates a form to update user account.
 *
 * @param User $entity The entity
 *
 * @return \Symfony\Component\Form\FormInterface The form
 */
private function updateUserAccountForm(User $user)
{
    $form = $this->createForm( AccountType::class, $user,
        array(
            'action' => $this->generateUrl('updateUserAccount'),
            'method' => 'POST',
        ));

    return $form;
}

还有 AccountType.php

class AccountType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('username', TextType::class)
        ->add('email', EmailType::class, array(
            'required' => true,
            'constraints' => array(
                new NotBlank(),
            )))
        ->add('password', PasswordType::class, array(
            'required' => true
            ))
        ->add('plainPassword', RepeatedType::class, array(
            'type' => PasswordType::class,
            'invalid_message' => 'The new password fields must match.',
            'required' => false,
            'first_options'  => array('label' => 'New Password'),
            'second_options' => array('label' => 'Confirm New Password')
        ))
        ->add('save', SubmitType::class, array('label' => 'Save ->'));
}

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        // enable/disable CSRF protection for this form
        'csrf_protection' => true,
        // the name of the hidden HTML field that stores the token
        'csrf_field_name' => '_token',
        // an arbitrary string used to generate the value of the token
        // using a different string for each form improves its security
        'csrf_token_id'   => 'reset_item',
        'data_class' => User::class
    ));
}
}

ajax 调用确实会返回一个序列化的表单。该表单已在配置文件功能中创建并且运行良好。调用“$form->isValid()”时收到表单错误消息

我一直在尝试以表单类型传递 $user 以使 Symfony 理解它基于预先存在的用户。但没有运气。

感谢任何帮助!

编辑:这是我的自定义 UniqueEmail Validator 类:

class UniqueEmailValidator extends ConstraintValidator
{
    /**
     * @var EntityManager
     */
    protected $em;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->em = $entityManager;
    }

    public function validate($value, Constraint $constraint)
    {
        // Do we have a pre registered user in DB from email form landing page?
        $userRepository = $this->em->getRepository(User::class);
        $existingUser = $userRepository->findOneByEmail($value);

        if ($existingUser && $existingUser->getIsActive()) {
            $this->context->buildViolation($constraint->message)
                ->setParameter('{{ string }}', $value)
                ->addViolation();
        }
    }
}

【问题讨论】:

  • UniqueEmail 是自定义约束验证,对吗?你能展示这个约束验证的代码吗?另外,请显示错误。
  • 您可能需要在 try/catch 中使用 UniqueConstraintViolationException ,我建议您阅读此内容:stackoverflow.com/questions/3967226/…
  • @FranckGamess 感谢您的评论,我已经编辑了发布 UniqueEmailValidator.php 文件的问题。触发的消息是我在 UniqueEmail.php 中的同一验证器文件夹中设置的消息。
  • @pbenard 谢谢,我知道这种情况是在调用 flush() 时发生的。这可以与表单验证一起使用吗?
  • 不,我不认为,您只需将刷新包装在 try\catch 中,如果错误被捕获,则使用 $form->addError 手动添加错误,并返回表单像这样。

标签: php symfony doctrine-orm symfony4


【解决方案1】:

据我所知,当您提交表单时,您在 email 字段上设置的 UniqueEmail 约束尝试使用您定义的规则验证提交的值。 假设您只想更新 用户名,表单将与存储在数据库中的 电子邮件字段 的当前值一起发送。当然,它会触发验证错误。

通常,最好在 user 表中的 email 列上设置唯一约束。因此,即使您不修改 email 字段,您也可以正确更新您的 User 实体。而且您将无法在数据库中创建与现有用户具有相同电子邮件的新用户。我想这就是你想要的。

Doctrine 可以帮助您实现这一目标。

在实体级别使用@UniqueConstraint

<?php
/**
 * @Entity
 * @Table(name="user_table",uniqueConstraints= {@UniqueConstraint(name="search_idx", columns={"email"})})
 */
class User implements UserInterface, \Serializable
{
}

@Column 在字段级别具有 unique 属性(我更喜欢这个,但取决于具体情况):

/**
 * @var string $email
 * @ORM\Column(type="string", length=32, unique=true, nullable=false)
 * @Assert\Email()
 * @Assert\NotBlank
 */
private $email;

看看它是否能解决您的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多