Symfony 4 - ограничение UniqueEntity не отображает сообщение об ошибке - PullRequest
0 голосов
/ 03 мая 2019

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

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

Ответы [ 2 ]

2 голосов
/ 04 мая 2019

Это происходит потому, что вы объявили, что поле 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(), чтобы избежать этой проблемы.

0 голосов
/ 06 мая 2019

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

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

/**
 * @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;
}

Надеюсь, это поможет.

...