I am trying to use Unique and Hostname constraint together in Syfony 5.4 entity. Lets have:
/**
* #ORM\Column(name="domain", type="string", length=150, nullable=false, unique=true)
* #Assert\NotBlank()
* #Assert\Hostname()
*/
private $domain;
This validate without problems. But when I add Unique constraint, it fails with message - This value should be of type array|IteratorAggregate.
/**
* #ORM\Column(name="domain", type="string", length=150, nullable=false, unique=true)
* #Assert\NotBlank()
* #Assert\Unique()
* #Assert\Hostname()
*/
private $domain;
I need both validation, yes, in this case it fails on database INSERT, but I want to validate form. How is correct way?
Thank you
DR
If I understand correctly you want to validate that the domain (or hostname) is unique inside the database table. In that case, you would have to use UniqueEntity see https://symfony.com/doc/current/reference/constraints/UniqueEntity.html.
It is worth mentioning that this doesn't protect you against race conditions.
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #UniqueEntity("domain")
*/
class YourEntity
{
/**
* #ORM\Column(name="domain", type="string", length=150, nullable=false, unique=true)
* #Assert\NotBlank()
* #Assert\Hostname()
*/
protected $domain;
}
Related
in my symfony project I have a groupValidators entity that is used to create a service within a company by indicating the name of the service and users...
GroupeValidateurs.php:
<?php
namespace App\Entity;
use Cocur\Slugify\Slugify;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass="App\Repository\GroupeValidateursRepository")
* #ORM\HasLifecycleCallbacks()
* #UniqueEntity(
* fields = {"nom"},
* message = "Ce nom a déjà été utilisé"
* )
*/
class GroupeValidateurs
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* Nom du service
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank
*/
private $nom;
/**
* Liste des utilisateurs membres du service
* #ORM\OneToMany(targetEntity="App\Entity\User", mappedBy="groupe")
*/
private $users;
/**
* #ORM\Column(type="string", length=255)
*/
private $slug;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Validateur", mappedBy="groupeValidateurs", orphanRemoval=true, cascade={"persist", "remove"})
* #ORM\OrderBy({"ordre" = "ASC"})
*/
private $validateurs;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Entreprise", inversedBy="services")
* #ORM\JoinColumn(nullable=true)
*/
private $entreprise;
And I want a company to be unable to enter the same service name twice (hence my #UniqueEntity), but I still want more than one company to use the same name for a service. For example company A can have a service called "Commerce", and company B too.
How could I do that? Because currently, if a company tries to create a service with a name already used for the service of another company, I have my constraint which intervenes
Assuming you have the id of the company and the name of the service in this entity, you can use unique constraint on two columns in your table definition:
/**
*
* #Table(name="GroupeValidateurs",
* uniqueConstraints={
* #UniqueConstraint(name="service_entreprise",
* columns={"service", "entreprise"})
* }
* )
*/
You should take care when you use french in your class name. People might think here you are speaking about Validator, which is not the case. I suggest you to rename your class GroupeValidateurs to something more speaking like Division or Department or Service. Also when you use a class name use singular.
What you need to do is add a constraint to ensure you don't have many times the same service name. You did that part correctly with your annotation.
Then you want :
One company has one service
One service can be related to many companies
So your problem is here
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Entreprise", inversedBy="services")
* #ORM\JoinColumn(nullable=true)
*/
private $entreprise;
You need to use OneToMany here as one service can be related to many companies
EDIT: I might have missread your question I didn't saw you seems to want a company can have not one service but two services. For this part we need you to provide your Company class.
I am configuring a series of ApiFilter classes from the Api-platform stack in a Symfony 4 application. I am finding that any filters using a nested property from an entity relationship takes no effect. Please see my example code below, would really appreciate any assistance with why it may not be working.
The entity I am trying to filter on is:
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Core\Annotation\ApiProperty;
/**
* PostAuthor
* #ApiResource()
* #ORM\Table(name="POST_AUTHOR", indexes={#ORM\Index(name="IDX_CF3397D94A7000A0", columns={"IDENTITY_ID"})})
* #ORM\Entity(repositoryClass="App\Repository\PostAuthorRepository")
*/
class PostAuthor
{
/**
* #var string
*
* #ORM\Column(name="USER_ID", type="string", length=255, nullable=false)
* #ORM\Id
*/
private $userId;
/**
* #var string
*
* #ORM\Column(name="USERNAME", type="string", length=255, nullable=false)
*/
private $username;
And the collection I am filtering is:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiFilter;
use Symfony\Component\Serializer\Annotation\Groups;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\NumericFilter;
use Doctrine\ORM\Mapping as ORM;
/**
* PostHeader
* #ORM\Table(name="POST_HEADER", indexes={#ORM\Index(name="IDX_1CEEE7D0B65E5FF8", columns={"EMOTION_REF_ID"}), #ORM\Index(name="IDX_1CEEE7D08AFAAB14", columns={"AUTHOR_ID"})})
* #ORM\Entity(repositoryClass="App\Repository\PostHeaderRepository")
* #ApiResource(
* normalizationContext={"groups"={"postheader:read"}},
* denormalizationContext={"groups"={"postheader:write"}},
* )
* #ApiFilter(SearchFilter::class, properties={"author.userId": "partial", "author.username": "partial"})
*/
class PostHeader
{
/**
* #var PostAuthor
*
* #ORM\ManyToOne(targetEntity="PostAuthor")
* #ORM\JoinColumns({#ORM\JoinColumn(name="AUTHOR_ID", referencedColumnName="USER_ID")})
* #Groups({"postheader:read"})
*/
private $author;
I just test on a new project with the same classes than you and it works. I can filter by author.userId.
Please give us your testing request. And the error message ;).
How can I set the starting value for an Auto Incremented like 100,101....
My code is
/**
* #ORM\Name
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="name", initialValue=100)
* #ORM\Column(type="integer")
*/
protected $name;
while trying i am getting an error like this
[Doctrine\Common\Annotations\AnnotationException]
[Semantical Error] The annotation "#Doctrine\ORM\Mapping\Name" in property
Epita\CrmBundle\Entity\CandidateAnalysisCrm::$name does not exist, or could
not be auto-loaded.
please help me anyone. Thanks for advance
My entity is:
namespace Epita\CrmBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* category
*
* #ORM\Table(name="candidateanalysiscrm")
* #ORM\Entity
*/
class CandidateAnalysisCrm {
/**
* #ORM\Id
* #ORM\Column(type = "integer", name= "id")
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #ORM\Column(type="integer", name="applicationinprogress",length=10,
nullable=true)
*/
protected $applicationinprogress;
/**
* #ORM\Name
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="name", initialValue=100)
* #ORM\Column(name="name", type="integer")
*/
protected $name;
}
You have several issues here:
First, as already mentioned there is no #Name doctrine annotation.
Second, the GeneratedValue annotation only applies if your attribute is annotated with the #Id annotation.
From the documentation
Specifies which strategy is used for identifier generation for an instance variable which is annotated by #Id. This annotation is optional and only has meaning when used in conjunction with #Id.
If this annotation is not specified with #Id the NONE strategy is used as default.
To fix it change your $name attributes annotations to:
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="SEQUENCE")
* #ORM\SequenceGenerator(sequenceName="name", initialValue=100)
* #ORM\Column(name="name", type="integer")
*/
protected $name;
}
If using the $name property as identifier is not an option for you, listening to doctrine events (e.g. prePersist) could be an alternative solution for your problem. But this is a totally different question.
I'm not sure, but i think that ORM\Name doesn't exist.
You have to write :
* #ORM\Column(name="column_name", type="string")
I am trying to override two properties of FOSUserBundle's User mapping.
use FOS\UserBundle\Model\User as BaseUser;
...
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
* #ORM\Table(name="user")
* #UniqueEntity(
* fields={"emailCanonical", "zone"},
* errorPath="email",
* message="This email address is already in use."
* )
* #ORM\AttributeOverrides({
* #ORM\AttributeOverride(name="email", column=#ORM\Column(type="string", name="email", length=255, unique=false, nullable=false)),
* #ORM\AttributeOverride(name="emailCanonical", column=#ORM\Column(type="string", name="emailCanonical", length=255, unique=false, nullable=false))
* })
*/
class User extends BaseUser
{
...
Everything seems to be working fine, except when I generate accessors: Invalid field override named 'email'
The error is thrown here:
// Doctrine\ORM\Mapping\ClassMetadataInfo
public function setAttributeOverride($fieldName, array $overrideMapping)
{
if ( ! isset($this->fieldMappings[$fieldName])) {
throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
}
Here, $this->fieldMappings only contains the fields of my child User and not of the parent (mapped superclass).
Anyone got a clue?
The way I managed to fix this issue with Invalid field override named [field] for class [class] was to also add the overwritten attribute in the class I overwritten it.
I'm using version Symfony v2.7.8 and Doctrine v1.6.1.
Example:
<?php
use FOS\UserBundle\Model\User as BaseUser;
/**
* #ORM\Entity
* #ORM\Table(name="user")
*
* #ORM\HasLifecycleCallbacks
* #ORM\AttributeOverrides({
* #ORM\AttributeOverride(name="usernameCanonical",
* column=#ORM\Column(
* type="string",
* name="username_canonical",
* length=255,
* unique=false
* )
* )
* })
*/
class User extends BaseUser
{
/**
* #var string
*
* #ORM\Column(type="string", name="username_canonical", length=255, unique=false)
*/
protected $usernameCanonical;
...
}
EDIT:
You're right, there is mapping info in FOSUserBundle/Resources/config/doctrine/model/User.orm.xml...
<mapped-superclass name="FOS\UserBundle\Model\User">
...
<field name="usernameCanonical" column="username_canonical" type="string" length="255" unique="true" />
<field name="email" column="email" type="string" length="255" />
Just guessing: Maybe this xml orm config is not parsed properly? Or maybe you need to do the overwrites in xml too?
I know this is an old post, but i found a sulution, at least it worked for me, maybe it would be helpful for someone else.
Well, i had the same issue and i'm using a custom manager for the fos_user, in the declaration file config.yml under doctrine entity_manager custom manager i declared the mapping to FOS_userBundle, BUT what was missing is to tell FOS_user that we use a diffrent manager and that's by adding :
fos_user:
---- db_driver: orm
---- model_manager_name: MyCustom_Manager
Sorry to revive an old post. What worked for me was to remove the ORM annotation on the field and ensure the name in AttributeOverride was the same as the field name being overridden.
E.g. the ff ORM annotation
/**
* #var string
*
* #ORM\Column(type="string", name="username_canonical", length=255, unique=false)
*/
protected $usernameCanonical;
Should become:
#ORM\AttributeOverride(name="usernameCanonical",
column=#ORM\Column(
type="string",
name="username_canonical",
length=255,
unique=false
)
)
Then, remove the ORM annotation to ensure we don't get a "Duplicate mapping for column" error. The final class looks like:
<?php
use FOS\UserBundle\Model\User as BaseUser;
/**
* #ORM\Entity
* #ORM\Table(name="user")
*
* #ORM\HasLifecycleCallbacks
* #ORM\AttributeOverrides({
* #ORM\AttributeOverride(name="usernameCanonical",
* column=#ORM\Column(
* type="string",
* name="username_canonical",
* length=255,
* unique=false
* )
* )
* })
*/
class User extends BaseUser
{
/**
* #var string
*/
protected $usernameCanonical;
}
I have a "Company" entity with three fields : id, name and slug. The name is unique.
When I attempt to create a new Company with a name already used I get the following error :
An exception occurred while executing 'INSERT INTO companies (name, slug) VALUES (?, ?)' with params ["test", "test-1"]:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'test' for key 'UNIQ_8244AA3A5E237E06'
I don't know why, but the #UniqueEntity does not show my error message. I also tried to put this line : #UniqueEntity(fields={"name"}, groups={"company"})
Here is the code of the class :
<?php
namespace Project\UserBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* Company
*
* #ORM\Table(name="companies")
* #UniqueEntity(fields={"name"}, message="form.error.name.unique")
* #ORM\Entity
*/
class Company
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=50, unique=true)
* #Assert\NotBlank(message="form.error.name.not_blank");
* #Assert\Length(min="4", max="50", minMessage="form.error.name.length.min {{ limit }}", maxMessage="form.error.name.length.max {{ limit }}");
*/
private $name;
/**
* #var string
*
* #ORM\Column(length=170, unique=true)
* #Gedmo\Slug(fields={"name"})
*/
private $slug;
/**
* #ORM\OneToMany(targetEntity="Project\UserBundle\Entity\User", mappedBy="company")
* #Assert\Valid
*/
private $users;
}
Could you test this 3 possibilities and try to get the default message? (But it seems that you already tried the third one. Maybe luck with the others)
(Btw, you can not set a groups option in the UniqueEntity annotation)
/**
* User
*
* #UniqueEntity("name")
* #UniqueEntity(fields="name")
* #UniqueEntity(fields={"name"})
*/
class User
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", unique=true)
*
* #Assert\NotBlank
*/
private $name;
}
This syntax works for me:
#UniqueEntity("name", message="form.error.name.unique")