Symfony 4-UniqueEntity约束不显示消息错误

时间:2019-05-03 14:02:22

标签: php symfony doctrine

我正在使用symfony 4.2和ReactJS。 我有一封带邮件的表格。这封邮件应该是唯一的。所以我在实体中包含一个UniqueEntity。

当我尝试使用该表单创建一个帐户时,问题如下,并向我抛出错误500:"An error occurred","hydra:description":"An exception occurred while executing \u0027INSERT INTO app_users (id, username, email, is_active, firstname, lastname, api_link_key, facebook_id, facebook_picture_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\u0027 with params XXXXXX : Unique violation: 7 ERROR: duplicate key value violates unique constraint \u0022uniq_c2502824f85e0677\u0022\nDETAIL: Key (username)=(XXXXXX) already exists." 所以我没有表格中的消息错误,也许是因为该错误500?还是应该将消息错误设置在某处?

在我的实体中,当我设置邮件字段时,我也将用户名字段设置为相同的值。 UniqueentEntity禁止在同一行的两个字段中具有相同的值吗?

我的实体:

* @ORM\Table(name="app_users")
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
* @UniqueEntity("email")
*/
class User implements UserInterface, \Serializable
{
   /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=254, unique=true)
     * @Encrypted
     */
    private $username;

    /**
     * @ORM\Column(type="string", length=255, unique=true)
     * @Assert\Email()
     * @Assert\NotBlank()
     * @Encrypted
     */
    private $email;

   [...]

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email): void
    {
        $this->email = $email;
        $this->username = $email;
    }
}

感谢您的帮助

3 个答案:

答案 0 :(得分:1)

之所以会发生这种情况,是因为您已声明字段username在基础数据库中必须是唯一的,但是您没有在应用程序中验证每个实体实例的username属性确实是唯一的。 -这就是为什么非唯一值到达数据库层的原因。

正如@Jakumi所评论的那样,您需要为您的实体类的username属性添加一个唯一的实体约束。您已经包含了email属性,因此只需将其扩展为也包含username

更改

@UniqueEntity("email")

@UniqueEntity("username")
@UniqueEntity("email")

忘记提及,请确保在类文件中包含UniqueEntity约束,否则将根本无法使用:

use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

最后,您可能会遇到的另一个问题是实体的用户名空白。由于username属性不可为空,因此将其假定为空白字符串。数据库层在唯一检查中不考虑空值,因为空值表示“根本没有定义任何值”,但由于空字符串是已定义的值,所以将检查空字符串。我建议您还验证username属性不能与@Assert\NotBlank()一起使用,以避免出现此问题。

答案 1 :(得分:1)

我使用Api平台,并且symfony @UniqueEntity正常工作。 您不需要自定义约束。

此注释对我有用。

* @UniqueEntity(
 *     fields={"username"},
 *     errorPath="username",
 *     groups={"ucreate", "screate"},
 *     message="This username is already in use"
 * )

您可以定义所需的所有验证组。例如,用于用户实体上POST操作的“ ucreate”验证组,以及用于具有用户实体(嵌入关系OneToOne)的学生实体上POST操作的“ screate”组。

On embed relations (just en example)
    /**
     * @ORM\OneToOne(targetEntity="App\Entity\User", cascade={"persist", "remove"})
     * @ORM\JoinColumn(nullable=true)
     * @Groups({"student:item:get", "student:write"})
     * @Assert\Valid(groups={"screate"})
     */
    private $user;

答案 2 :(得分:0)

最后,我创建了一个自定义验证器,该验证器将检索所有电子邮件并检查当前电子邮件是否尚未存储在数据库中。

约束:

/**
 * @Annotation
 */
class DuplicateUser extends Constraint
{
    public $message = 'Email already exists.';
}

约束验证器:

class DuplicateUserValidator extends ConstraintValidator
{
    private $em;
    private $encryptor;

    /**
     * DuplicateUserValidator constructor.
     */
    public function __construct(EntityManagerInterface $em, Encryptor $encryptor)
    {
        $this->em = $em;
        $this->encryptor = $encryptor;
    }

    public function validate($value, Constraint $constraint)
    {
        if (!$constraint instanceof DuplicateUser) {
            throw new UnexpectedTypeException($constraint, DuplicateUser::class);
        }

       // custom constraints should ignore null and empty values to allow
        // other constraints (NotBlank, NotNull, etc.) take care of that
        if (null === $value || '' === $value) {
            return;
        }

        if (!is_string($value)) {
            // throw this exception if your validator cannot handle the passed type so that it can be marked as invalid
            throw new UnexpectedValueException($value, 'string');

            // separate multiple types using pipes
            // throw new UnexpectedValueException($value, 'string|int');
        }

        # Email is encrypted in database, we need to encrypt request's email value before.
        $encryptedEmail = $this->encryptor->encryptWithMarker($value);

        $qb = $this->em->createQueryBuilder();

        $qb->select('u.email')
            ->from(User::class, 'u');

        $arrayEmails = $qb->getQuery()->execute();

        foreach ($arrayEmails as $key => $arrayEmail) {
            foreach ($arrayEmail as $email) {
                if ($encryptedEmail == $email) {
                    $this->context->buildViolation($constraint->message)
                        ->addViolation();
                }
            }
        }
    }
}

在实体中,我添加了@CustomAssert \ DuplicateUser(不要忘记添加use App\Validator\Constraints as CustomAssert;):

* @ORM\Table(name="app_users")
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
* @UniqueEntity("email")
*/
class User implements UserInterface, \Serializable
{
    /**
     * @ORM\Column(type="string", length=255, unique=true)
     * @Assert\Email()
     * @Assert\NotBlank()
     * @Encrypted
     * @CustomAssert\DuplicateUser
    */
    private $email;
}

希望它会有所帮助。