Symfony 4 - UniqueEntity 约束不显示消息错误
Posted
技术标签:
【中文标题】Symfony 4 - UniqueEntity 约束不显示消息错误【英文标题】:Symfony 4 - UniqueEntity constraint doesn't display message error 【发布时间】:2019-09-22 02:15:12 【问题描述】:我正在使用 symfony 4.2 和 ReactJS。 我有一个带有邮件的表格。这封邮件应该是唯一的。所以我在 Entity 中包含了一个 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 ?或者也许我应该在某处设置消息错误?
在我的实体中,当我设置邮件字段时,我也将用户名字段设置为相同的值。 UniqueEntity 禁止在同一行的两个字段中具有相同的值?
我的实体:
* @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;
感谢您的帮助
【问题讨论】:
在将数据保存到数据库之前,您是否检查了表单/实体验证?像 $form->isValid() 或 $validator->validate($user) 前面是用ReactJS生成的,我用的是api-platform。所以我没有一个控制器来验证数据。 NotBlank 断言正在工作,因为我无法在没有数据的情况下发布表单 尝试使用验证组创建帐户并将 UniqueEntity 约束添加到验证组 SQL 错误不是抱怨您的email
列,而是抱怨您也声明为唯一的用户名。所以你可能想要做的是添加一个额外的约束,它也强制用户名的唯一性:@UniqueEntity("username")
嘿,您是否将基本用法部分(代码示例下方)symfony.com/doc/current/reference/constraints/UniqueEntity.html 中的警告标记为红色并检查过,这些警告不会对您的情况造成问题?
【参考方案1】:
发生这种情况是因为您已声明字段 username
在底层数据库中必须是唯一的,但您没有在应用程序中验证每个实体实例的 username
属性确实是唯一的。 -- 这就是非唯一值到达数据库层的原因。
正如@Jakumi 所评论的,您需要为您的实体类的username
属性添加一个唯一的实体约束。您已经包含了email
属性,因此您只需将其扩展为也包含username
。
改变
@UniqueEntity("email")
到
@UniqueEntity("username")
@UniqueEntity("email")
忘了提一下,确保在类文件中包含UniqueEntity
约束,否则它根本不起作用:
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
最后,您可能会遇到的另一个问题是实体的用户名是空白的。因为username
属性不可为空,所以将假定它是一个空白字符串。数据库层在唯一检查中不考虑空值,因为 null 表示“根本没有定义值”,但会检查空字符串,因为空字符串是已定义的值。我建议您还验证username
属性不能为@Assert\NotBlank()
的空白以避免此问题。
【讨论】:
根据@UniqueEntity
fields 的手册 如果您需要两个字段分别唯一(例如,唯一的电子邮件和唯一的用户名),您可以使用两个 UniqueEntity 条目,每个条目带有单个字段。
谢谢,但在您发表评论之前的 12 分钟内,我已经对其进行了编辑。
我引用了您的解释来源,因为UniqueEntity
可以接受fields
的数组或字符串。 eg:@UniqueEntity("email", "username")
效果不一样。
您好,感谢您的帮助。我在用户名字段上删除了 unique=true ,因为它不是强制性的。但我现在仍然在电子邮件字段上收到错误:ERROR: duplicate key value violates unique constraint \u0022uniq_c2502824f85e0677\u0022\nDETAIL: Key (email)=(XXXXXX) already exists.
【参考方案2】:
最后,我创建了一个自定义验证器,用于检索所有电子邮件并检查当前电子邮件是否已在数据库中。
约束:
/**
* @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;
希望对你有帮助。
【讨论】:
一旦您的数据库中的电子邮件数量增加,您检查重复的实施将变得非常缓慢。使用 where 语句直接检查新邮件效率更高。【参考方案3】:我使用 Api 平台和 symfony @UniqueEntity 工作正常。 您不需要自定义约束。
这个注释对我有用。
* @UniqueEntity(
* fields="username",
* errorPath="username",
* groups="ucreate", "screate",
* message="This username is already in use"
* )
您可以定义您想要的所有验证组。例如,“ucreate”验证组用于用户实体上的 POST 操作,“screate”组用于具有用户实体(嵌入关系 OneToOne)的学生实体上的 POST 操作。
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;
【讨论】:
以上是关于Symfony 4 - UniqueEntity 约束不显示消息错误的主要内容,如果未能解决你的问题,请参考以下文章
Symfony UniqueEntity vs UniqueConstraint vs unique=true
FOSUserBundle:UniqueEntity 注释抛出:字段“用户”没有被 Doctrine 映射,因此无法验证其唯一性