Symfony 4 - ограничение UniqueEntity не отображает ошибку сообщения

Я использую 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;
    }
}

Спасибо за вашу помощь

Перед сохранением данных в базу данных проверяете ли вы проверку формы/сущности? например $form->isValid() или $validator->validate($user)

stephan.mada 03.05.2019 16:28

Фронт создается с помощью ReactJS, и я использую API-платформу. Поэтому у меня нет контроллера для проверки данных. Утверждение NotBlank работает, потому что я не могу опубликовать форму без данных

Alexis 03.05.2019 16:35

попробуйте использовать группы проверки для создания учетной записи и добавьте ограничения UniqueEntity в группы проверки.

stephan.mada 03.05.2019 16:39

Ошибка SQL жалуется не на ваш столбец email, а на имя пользователя, которое вы также объявили уникальным. Итак, что вы, вероятно, захотите сделать, так это добавить дополнительное ограничение, которое также обеспечивает уникальность имени пользователя: @UniqueEntity("username")

xabbuh 03.05.2019 16:47

@xabbuh Я удалил уникальное = true в поле имени пользователя. Затем я получил ту же ошибку, но в поле электронной почты

Alexis 03.05.2019 16:55

эй, вы проверили предупреждения в разделе основного использования (ниже примера кода) symfony.com/doc/current/reference/constraints/UniqueEntity.h‌​tml и проверили, что они не вызовут проблем в вашем случае?

Jakumi 03.05.2019 18:20

Привет @Alexis, посещая комментарий xabbuh, пожалуйста, опубликуйте правильное сообщение об ошибке. Это поможет другим найти решение.

SilvioQ 03.05.2019 20:44
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
7
4 294
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Это происходит потому, что вы объявили, что поле 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, каждая с одним полем.

Will B. 04.05.2019 00:40

Спасибо, но я уже отредактировал это за 12 минут до вашего комментария.

Adambean 04.05.2019 00:42

Я цитировал источник вашего объяснения, поскольку UniqueEntity может принимать массив или строку для fields. например: @UniqueEntity({"email", "username"}) который не имеет такого же эффекта.

Will B. 04.05.2019 00:53

Привет, спасибо за вашу помощь. Я убрал уникальное=true в поле имени пользователя, потому что это не обязательно. Но я все еще получаю сообщение об ошибке в поле электронной почты: ERROR: duplicate key value violates unique constraint \u0022uniq_c2502824f85e0677\u0022\nDETAIL: Key (email)=(XXXXXX) already exists.

Alexis 06.05.2019 09:54
Ответ принят как подходящий

Наконец, я создал собственный валидатор, который извлекает все электронные письма и проверяет, что текущего еще нет в базе данных.

Ограничение:

/**
 * @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.

Thomas Rückert 15.06.2020 10:18

Я использую платформу 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;

Другие вопросы по теме