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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With