Я использую Symfony 4.2 и ReactJS. У меня есть форма с почтой. Эта почта должна быть уникальной. Итак, у меня есть UniqueEntity, включенная в Entity.
Проблема в следующем, когда я пытаюсь создать учетную запись с помощью формы, она выдает мне ошибку 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;
}
}
Спасибо за вашу помощь
Фронт создается с помощью ReactJS, и я использую API-платформу. Поэтому у меня нет контроллера для проверки данных. Утверждение NotBlank работает, потому что я не могу опубликовать форму без данных
попробуйте использовать группы проверки для создания учетной записи и добавьте ограничения UniqueEntity в группы проверки.
Ошибка SQL жалуется не на ваш столбец email, а на имя пользователя, которое вы также объявили уникальным. Итак, что вы, вероятно, захотите сделать, так это добавить дополнительное ограничение, которое также обеспечивает уникальность имени пользователя: @UniqueEntity("username")
@xabbuh Я удалил уникальное = true в поле имени пользователя. Затем я получил ту же ошибку, но в поле электронной почты
эй, вы проверили предупреждения в разделе основного использования (ниже примера кода) symfony.com/doc/current/reference/constraints/UniqueEntity.html и проверили, что они не вызовут проблем в вашем случае?
Привет @Alexis, посещая комментарий xabbuh, пожалуйста, опубликуйте правильное сообщение об ошибке. Это поможет другим найти решение.






Это происходит потому, что вы объявили, что поле 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 поля Если вам нужно, чтобы два поля были уникальными по отдельности (например, уникальный адрес электронной почты и уникальное имя пользователя), вы используете две записи UniqueEntity, каждая с одним полем.
Спасибо, но я уже отредактировал это за 12 минут до вашего комментария.
Я цитировал источник вашего объяснения, поскольку UniqueEntity может принимать массив или строку для fields. например: @UniqueEntity({"email", "username"}) который не имеет такого же эффекта.
Привет, спасибо за вашу помощь. Я убрал уникальное=true в поле имени пользователя, потому что это не обязательно. Но я все еще получаю сообщение об ошибке в поле электронной почты: ERROR: duplicate key value violates unique constraint \u0022uniq_c2502824f85e0677\u0022\nDETAIL: Key (email)=(XXXXXX) already exists.
Наконец, я создал собственный валидатор, который извлекает все электронные письма и проверяет, что текущего еще нет в базе данных.
Ограничение:
/**
* @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.
Я использую платформу Api, и symfony @UniqueEntity работает нормально. Вам не нужно пользовательское ограничение.
Эта аннотация работает для меня.
* @UniqueEntity(
* fields = {"username"},
* errorPath = "username",
* groups = {"ucreate", "screate"},
* message = "This username is already in use"
* )
Вы можете определить все группы проверки, которые вы хотите. Например, группа проверки «ucreate» для действий POST в объекте «Пользователь» и группа «создать» для действий POST в объекте «Студент», который имеет объект «Пользователь» (встроенное отношение OneToOne).
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;
Перед сохранением данных в базу данных проверяете ли вы проверку формы/сущности? например $form->isValid() или $validator->validate($user)