Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unique Constraint return type mismatch

I have this User entity.

<?php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * @ApiResource(
 *     collectionOperations={
 *          "register"={
 *              "method"="POST",
 *              "path"="/users/register",
 *              "denormalization_context"={"groups"={"register"}},
 *              "normalization_context"={"groups"={"read"}},
 *              "validation_groups"={"register"},
 *              "swagger_context"={
 *                  "summary"="Register a user",
 *                  "description"="For anonymous user to register an account."
 *              }
 *          }
 *     }
 * )
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 */
class User implements UserInterface
{
    const ROLE_USER = 'ROLE_USER';
    const ROLE_WEBADMIN = 'ROLE_WEBADMIN';
    const ROLE_SUPERADMIN = 'ROLE_SUPERADMIN';

    const DEFAULT_ROLES = [self::ROLE_USER];

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     * @Groups({"read"})
     */
    private $id;

    /**
     * @Assert\Unique(groups={"write","register"})
     * @ORM\Column(type="string", length=180, unique=true)
     * @Assert\Length(min="6", max="255")
     * @Groups({"read", "write", "register"})
     */
    private $username;

    /**
     * @Assert\Unique(groups={"write","register"})
     * @ORM\Column(type="string", length=255, unique=true)
     * @Assert\Email(groups={"write","register"})
     * @Assert\NotBlank(groups={"register"})
     * @Assert\Length(min=6, max=255, groups={"write","register"})
     * @Groups({"write","register"})
     */
    private $email;

    /**
     * @ORM\Column(type="json")
     * @Groups({"read"})
     */
    private $roles = [];

    /**
     * @var string The hashed password
     * @ORM\Column(type="string")
     * @Assert\NotBlank(groups={"write", "register"})
     * @Assert\Regex(
     *     pattern="/(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{7,}/",
     *     message="Password must be minimum seven characters long and contain at least one digit, one upper case letter, and one lower case letter"
     * )
     * @Groups({"write", "register"})
     */
    private $password;

    /**
     * @Groups({"register"})
     * @Assert\NotBlank(groups={"register"})
     * @Assert\Expression(
     *     "this.getPassword() === this.getRetypePassword()",
     *     message="Passwords does not match",
     *     groups={"register"}
     * )
     */
    private $retypePassword;

    /**
     * @ORM\Column(name="password_change_date", type="integer", nullable=true)
     */
    private $passwordChangeDate;

    /**
     * @var string Full name
     * @ORM\Column(type="string", length=255)
     * @Assert\NotBlank()
     * @Groups({"read", "write", "register"})
     */
    private $name;

    /**
     * @ORM\Column(name="is_active", type="boolean", options={"default": 0} )
     * @Groups({"read"})
     */
    private $isActive;

    /**
     * @ORM\Column(name="confirmation_token", type="string", length=40, nullable=true)
     */
    private $confirmationToken;

    /**
     * User constructor.
     */
    public function __construct()
    {
        $this->setIsActive(false)
            ->setConfirmationToken(NULL)
            ->setRoles(self::DEFAULT_ROLES);
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    /**
     * A visual identifier that represents this user.
     *
     * @see UserInterface
     */
    public function getUsername(): string
    {
        return (string) $this->username;
    }

    public function setUsername(string $username): self
    {
        $this->username = $username;

        return $this;
    }

    /**
     * @see UserInterface
     */
    public function getRoles(): array
    {
        $roles = $this->roles;
        // guarantee every user at least has ROLE_USER
        $roles[] = 'ROLE_USER';

        return array_unique($roles);
    }

    public function setRoles(array $roles): self
    {
        $this->roles = $roles;

        return $this;
    }

    /**
     * @see UserInterface
     */
    public function getPassword(): string
    {
        return (string) $this->password;
    }

    public function setPassword(string $password): self
    {
        $this->password = $password;

        return $this;
    }

    /**
     * @see UserInterface
     */
    public function getSalt()
    {
        // not needed when using the "bcrypt" algorithm in security.yaml
    }

    /**
     * @see UserInterface
     */
    public function eraseCredentials()
    {
        // If you store any temporary, sensitive data on the user, clear it here
        // $this->plainPassword = null;
    }

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

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function getIsActive(): ?bool
    {
        return $this->isActive;
    }

    public function setIsActive(bool $isActive): self
    {
        $this->isActive = $isActive;

        return $this;
    }

    public function getConfirmationToken(): ?string
    {
        return $this->confirmationToken;
    }

    public function setConfirmationToken(?string $confirmationToken): self
    {
        $this->confirmationToken = $confirmationToken;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getPasswordChangeDate()
    {
        return $this->passwordChangeDate;
    }

    /**
     * @param mixed $passwordChangeDate
     */
    public function setPasswordChangeDate($passwordChangeDate): void
    {
        $this->passwordChangeDate = $passwordChangeDate;
    }

    /**
     * @return string
     */
    public function getRetypePassword(): string
    {
        return $this->retypePassword;
    }

    /**
     * @param string $retypePassword
     */
    public function setRetypePassword(string $retypePassword): void
    {
        $this->retypePassword = $retypePassword;
    }
}

When I request (via Insomnia, postman-like api tools, headers already set)

{
  "username": "cerseilann",
  "password": "password",
  "email": "[email protected]",
  "name": "Cersei Lannister Duplicate",
  "retypePassword": "password"
}

I'm expecting some error message related with unique value. But instead, I receive this error:

This value should be of type array|IteratorAggregate

Full response

{
  "@context": "\/api\/contexts\/ConstraintViolationList",
  "@type": "ConstraintViolationList",
  "hydra:title": "An error occurred",
  "hydra:description": "username: This value should be of type array|IteratorAggregate.\nemail: This value should be of type array|IteratorAggregate.",
  "violations": [
    {
      "propertyPath": "username",
      "message": "This value should be of type array|IteratorAggregate."
    },
    {
      "propertyPath": "email",
      "message": "This value should be of type array|IteratorAggregate."
    }
  ]
}

What did I miss?

Edit

When I use UniqueEntity and remove the Unique

...
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 * @UniqueEntity(fields="username", message="Username {{ value }} already taken. Please use another username.")
 * @UniqueEntity(fields="email", message="Email {{ value }} already being used. COnsider forgot password to retrieve the password.")
 */

...

I got this return error

{
  "@context": "\/api\/contexts\/Error",
  "@type": "hydra:Error",
  "hydra:title": "An error occurred",
  "hydra:description": "An exception occurred while executing 'INSERT INTO user (username, email, roles, password, password_change_date, name, is_active, confirmation_token) VALUES (?, ?, ?, ?, ?, ?, ?, ?)' with params [\"cerseilann\", \"[email protected]\", \"[\\\u0022ROLE_USER\\\u0022]\", \"$argon2id$v=19$m=65536,t=4,p=1$0Wu+2mjS\\\/Uft6lu2ieJOMQ$6ostmakEH2LKYKVvr5PYCoFOZvDwwNT\\\/\\\/+xI+g9hkG8\", null, \"Cersei Lannister 1\", 0, \"adde39972ac50feb3918\"]:\n\nSQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'cerseilann' for key 'UNIQ_8D93D649F85E0677'",
  "trace": [

...

I'm expecting the error message to be appear.

like image 332
Permana Avatar asked Sep 16 '19 00:09

Permana


1 Answers

The Unique constraint is meant for collections (meaning: a field, that holds a collection), and determines, if all entries are unique.

What you probably want:

The UniqueEntity constraint - it must be set on the entity, not the fields, but you can set it twice and once for each field, and set its fields property to the appropriate field respectively.

like image 165
Jakumi Avatar answered Oct 31 '22 10:10

Jakumi