Symfony2 UniqueEntity validation error with entity inheritance - validation

I have a patner, a buyer and an admin class that inherit a user class
When I want to add a partner, the validator do not work
* #DoctrineAssert\UniqueEntity(fields="username", message="Ce nom d'utilisateur est déjà utilisé, veuillez en choisir un autre.", groups={"registration", "account"})
* #DoctrineAssert\UniqueEntity(fields="mail", message="Cette adresse mail est déjà utilisé, veuillez en choisir un autre.", groups={"registration", "account"})
If I choose a username to a "partner" that is already in database, it shows me the right validation error. But if I choose the username of a "buyer" which is already in the database, there is no verification, and have a unique field error in my databases.
Class User
<?php
namespace Antho\Test\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints as DoctrineAssert;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder;
/**
* Antho\Test\CoreBundle\Entity\User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="Antho\Test\CoreBundle\Entity\UserRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({"user" = "User", "partner" = "Partner", "buyer" = "Buyer", "admin" = "Admin"})
* #ORM\HasLifecycleCallbacks()
* #DoctrineAssert\UniqueEntity(fields="username", message="Ce nom d'utilisateur est déjà utilisé, veuillez en choisir un autre.", groups={"registration", "account"})
* #DoctrineAssert\UniqueEntity(fields="mail", message="Cette adresse mail est déjà utilisé, veuillez en choisir un autre.", groups={"registration", "account"})
*/
class User implements UserInterface
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $username
*
* #ORM\Column(name="username", type="string", length=255, unique=true)
*/
private $username;
/**
* #var string $lastName
*
* #ORM\Column(name="last_name", type="string", length=255)
*/
private $lastName;
/**
* #var string $firstName
*
* #ORM\Column(name="first_name", type="string", length=255)
*/
private $firstName;
/**
* #var string $mail
*
* #ORM\Column(name="mail", type="string", length=255, unique=true)
*/
private $mail;
/**
* #var string $password
*
* #ORM\Column(name="password", type="string", length=255)
*/
private $password;
public function __construct()
{
if ($this->createdAt === null) {
$this->createdAt = new \DateTime('now');
}
$this->isEnabled = true;
}
public function __toString()
{
return $this->username;
}
GETTER and SETTER ...
}
Class Partner
<?php
namespace Antho\Test\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints as DoctrineAssert;
/**
* Antho\Test\CoreBundle\Entity\Partner
*
* #ORM\Table(name="partner")
* #ORM\Entity(repositoryClass="Antho\Test\CoreBundle\Entity\PartnerRepository")
*/
class Partner extends User
{
/**
* #ORM\OneToMany(targetEntity="Restaurant", mappedBy="partner", cascade={"remove", "persist"})
*/
private $restaurants;
/**
* #var string $company
*
* #ORM\Column(name="company", type="string", length=255)
*/
private $company;
public function __construct()
{
parent::__construct();
$this->restaurants = new \Doctrine\Common\Collections\ArrayCollection();
$this->isValid = false;
}
GETTER and SETTER ...
}
Class Buyer
<?php
namespace Antho\Test\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints as DoctrineAssert;
/**
* Antho\Test\CoreBundle\Entity\Buyer
*
* #ORM\Table(name="buyer")
* #ORM\Entity(repositoryClass="Antho\Test\CoreBundle\Entity\BuyerRepository")
*/
class Buyer extends User
{
/**
* #var string $address
*
* #ORM\Column(name="address", type="string", length=255)
*/
private $address;
/**
* #var string $city
*
* #ORM\Column(name="city", type="string", length=255)
*/
private $city;
/**
* #var string $zip
*
* #ORM\Column(name="zip", type="string", length=255)
*/
private $zip;
/**
* #var boolean $newsletter
*
* #ORM\Column(name="newsletter", type="boolean")
*/
private $newsletter;
public function __construct()
{
parent::__construct();
if ($this->newsletter === null) {
$this->newsletter = false;
}
}
GETTER and SETTER ...
}

This is an old unanswered question, however people still might face this issue. And this might help and save their time.
The problem is that UniqueEntityValidator by default takes only current (the one which is checking) entity's repository. In other words, if you submit Partner, it checks only Partner entries. It doesn't take into account other entites, such as Buyer or User in your "single_table" inheritance mapping.
To solve your problem, you can just add to your annotation entityClass attribute with your parent class name:
#DoctrineAssert\UniqueEntity(fields="username", message="Ce nom d'utilisateur est déjà utilisé, veuillez en choisir un autre.", groups={"registration", "account"}, entityClass="Antho\Test\CoreBundle\Entity\User")
This part entityClass="Antho\Test\CoreBundle\Entity\User" actually solves your problem.
In this case validator will go through all your user entries (including its child entries too).

My two cents. Not pretend to be the right answer.
Remove all unneeded #Table from the subclasses and make fields visible (protected). I did quite the same (with Symfony 2.0.x) and it works like a charm.
It's slightly different from your code, here a tag (or a keyword) name is unique for each user. But you can test it anyways:
/**
* #ORM\Entity
* #ORM\Table(
* name="meta",
* uniqueConstraints={
* #ORM\UniqueConstraint(columns={"name", "user_id", "type"})
* },
* indexes={
* #ORM\index(columns={"description"}),
* #ORM\index(columns={"type"})
* }
* )
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({"tag" = "Tag", "keyword" = "Keyword"})
* #UniqueEntity(fields={"name", "user"})
*/
abstract class Meta
{
protected $name;
protected $user;
}
Child classes:
/**
* #ORM\Entity
*/
class Tag extends Meta { }
/**
* #ORM\Entity
*/
class Keyword extends Meta { }

Related

Doctrine in laravel, getting all user's roles

I have a simple project in laravel 5.4 with Doctrine 2.0. I have three tables: users, roles, user_roles. Screenshot from tables schemas from HEIDISQL I show below:
users:
roles:
user_roles:
I have three Entities classes for each table of course:
<?php
namespace TodoList\Http\Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="users")
*/
class User
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*
*/
private $id;
/**
*
* #ORM\Column(type="string")
*
*/
private $name;
/**
*
* #ORM\Column(type="string")
*
*/
private $email;
/**
* #ORM\OneToMany(targetEntity="Task", mappedBy="user", cascade={"persist"})
* #var ArrayCollection|Task[]
*/
private $tasks;
/**
* #ORM\OneToMany(targetEntity="UserRole", mappedBy="user")
*/
protected $user_roles;
/**
* User constructor
* #param #name
* #param #email
* #param $password
*/
public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
$this->tasks = new ArrayCollection();
$this->user_roles = new ArrayCollection();
}
/**
*
* #return mixed
*
*
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getEmail()
{
return $this->email;
}
/**
* #return mixed
*/
public function getTasks()
{
return $this->tasks;
}
public function addTask(Task $task)
{
if(!$this->tasks->contains($task)) {
$task->setUser($this);
$this->tasks->add($task);
}
}
public function getId(){
return $this->id;
}
public function getUserRoles(){
return $this->user_roles;
}
}
<?php
namespace TodoList\Http\Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="roles")
*
*/
class Role{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string")
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="description", type="text")
*/
private $description;
/**
* #ORM\OneToMany(targetEntity="UserRole", mappedBy="role",cascade={"persist"})
*/
protected $user_roles;
public function getId(){
return $this->id;
}
public function getName(){
return $this->name;
}
public function getDescription(){
return $this->description;
}
}
<?php
namespace TodoList\Http\Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="user_roles")
*/
class UserRole
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #ORM\ManyToOne(targetEntity="User", inversedBy="user_roles")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*
*/
private $user;
/**
*
* #ORM\ManyToOne(targetEntity="Role", inversedBy="user_roles")
* #ORM\JoinColumn(name="role_id", referencedColumnName="id")
*/
private $role;
public function setUser(User $user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \NVC\UserBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* Set recipe
*
* #param \NVC\RecipeBundle\Entity\Recipe $recipe
* #return UserRecipeAssociation
*/
public function setRole(Role $role)
{
$this->role = $role;
return $this;
}
/**
* Get recipe
*
* #return \NVC\RecipeBundle\Entity\Recipe
*/
public function getRole()
{
return $this->role;
}
}
I have a problem when I'm trying retrieve roles of particular user by doctrine Entity Managar. I'm trying to do that in that way:
public function showUserRoles(EntityManagerInterface $em){
$userRoles = $em->getRepository('\TodoList\Http\Entities\UserRole');
$userRoles->findBy(['user' => 2]);
//count($userRole);
}
There is an error from method findBy
Error message is:
(1/1) FatalErrorException
Doctrine\Common\Proxy\AbstractProxyFactory::getProxyDefinition():
Failed opening required
'C:\xampp\htdocs\Project\storage\proxies__CG__TodoListHttpEntitiesRole.php'
(include_path='C:\xampp\php\PEAR')
Is anything wrong with my Entities? I don't know how could I solve that problem. Could someone help me with that?
I would be very greateful
Best regards ;)
You need to set folder C:\xampp\htdocs\Project\storage\proxies\ as writable and then you need to generate proxies by console command:
doctrine:generate:proxies

Error While Creating the schema for doctroin

I have tried to create the database schema by running the artisan command through CLI and got the following error:
The annotation "#Doctrine\ORM\Mapping\Names" in property App\Entity\Vouchers::$names does not exist, or could not be auto-loaded.
Following the Code for my Entity.
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="vouchers")
**/
class Vouchers
{
/**
* #var integer $id
* #ORM\Column(name="id", type="integer", unique=true, nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
*/
private $id;
/**
* #var string $names
* #ORM\Column(name="names", type="string", unique=false, nullable=false)
* #ORM\Names
*
*/
private $names;
/**
* #var string $amount
* #ORM\Column(name="amount", type="string", unique=false, nullable=false)
* #ORM\Amount
*
*/
private $amount;
//Getter And Setters
//ID
public function getId()
{
return $this->id;
}
//name
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
//amount
public function getAmount()
{
return $this->amount;
}
public function setAmount($amount)
{
$this->amount = $amount;
}
}
Please check and let me know if there is any mistake i have made in the above code and how it can be fixed. I have tried many other solution but still i am not able to figure this out.
Fixed it by replacing the name section with the following
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=150, nullable=false)
*/
private $name;

Doctrine generate Models. Metadata Configuration

I generated Entities with Doctrine's generating method inside Doctrine.php from CodeIgniter Controller:
function generate_classes(){
$this->em->getConfiguration()
->setMetadataDriverImpl(
new DatabaseDriver(
$this->em->getConnection()->getSchemaManager()
)
);
$cmf = new DisconnectedClassMetadataFactory();
$cmf->setEntityManager($this->em);
$metadata = $cmf->getAllMetadata();
$generator = new EntityGenerator();
$generator->setUpdateEntityIfExists(true);
$generator->setGenerateStubMethods(true);
$generator->setGenerateAnnotations(true);
$generator->generate($metadata, APPPATH."models/Entities");
}
The generated Entity had this syntax:
use Doctrine\ORM\Mapping as ORM;
/**
* Users
*
* #ORM\Table(name="users")
* #ORM\Entityz
*/
class Users
{
/**
* #var integer
*
* #ORM\Column(name="idusers", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $idusers;
/**
* #var string
*
* #ORM\Column(name="first_name", type="string", length=45, nullable=true)
*/
private $firstName;
/**
* #var string
*
* #ORM\Column(name="last_name", type="string", length=45, nullable=true)
*/
Now calling these Entities form my model didn't work. The model didn't find the entity. After manually changing the syntax to the following code example, I got it to work:
use Doctrine\ORM\Mapping as ORM;
/**
* Users
*
* #Entity #Table(name="users")
*/
class Users
{
/**
* #var integer
*
* #Column(name="idusers", type="integer", nullable=false)
* #Id
*
*/
private $idusers;
/**
*
* #GeneratedValue
* #Column(name="first_name", type="string", length=45, nullable=true)
*/
private $firstName;
And now everything works!
I am not going to manually change every Entity.
How can I configure Doctrine to produce Entity's with the correct syntax. As used in their tutorials as well? http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html
Noob question. Please help!
Found the solution:
changed in application/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php the constructor from:
public function __construct()
{
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
$this->annotationsPrefix = 'ORM\\';
}
}
To:
public function __construct()
{
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
$this->annotationsPrefix = '';
}
}

symfony 2 formbuilder entity error validation

I build a form with
$form = $this->createFormBuilder($user)
->add('locations', 'entity', array('class' =>'PrUserBundle:Location',
'property' => 'name',
'query_builder' => function(\Doctrine\ORM\EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.client_id = :client_id')
->setParameter('client_id', $this->clientId)
->orderBy('u.name', 'ASC');
},'required' => true, 'multiple' => true, 'expanded' => true)
)
After submitting,I would like to validate the form. This is done by a validation.yml
Pr\UserBundle\Entity\User:
properties:
usergroups:
- NotBlank: ~
message: You must select a location
I only get a error
Unable to parse at line 15 (near " message: You must select a location").
EDIT: This has been fixe for removing the ~ tilde like Rooneyl already told me (see comment)
How can I implement the validation of this field inside the form?
EDIT: It's a bit more complicativ I guess.
There are two entities. User and e.g. Location.
The User entity contains
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="integer", nullable=true)
*/
protected $client;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $client_id;
/**
* #ORM\Column(type="string", length=16383, nullable=true) //16383 = max varchar utf8
*/
private $imageurl;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
protected $firstname;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
protected $lastname;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
protected $emailalert;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $phone;
/**
* #ORM\Column(type="integer", nullable = true)
*/
protected $lock_state;
/**
* #ORM\Column(type="array", nullable=true)
*/
private $locations;
/**
* #ORM\Column(type="array", nullable=true)
*/
private $usergroups;
/**
* #ORM\Column(type="string", length=5, options={"fixed" = true, "default" = "de_DE"})
*/
private $locale = 'de_DE';
/**
* #ORM\Column(type="string", length=32)
*/
private $timezone = 'UTC';
/**
* #ORM\Column(type="array", nullable=true)
*/
private $created='1';
For all those fields, I can get a validation. But for the field "locations" I dont get a validation. This will be data from the entity "Location" like you can see below:
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #ORM\Column(type="string", length=20)
*/
public $name; //Raumbezeichnung, Ausgabelinie-Bezeichnung, Freitextbezeichnung, Standortname
/**
* #ORM\Column(type="integer")
*/
public $type;
/**
* #ORM\Column(type="string", length=20)
*/
public $parentLocation;
/**
* #ORM\Column(type="integer", nullable=true)
*/
public $parentlocation_id;
/**
* #ORM\Column(type="integer", nullable=true)
*/
public $client;
/**
* #ORM\Column(type="integer")
*/
public $client_id;
I dont know if I can display this by a many2many connect in the entity, but actually I used to go the way I discribed above (I'm a noob in symfony until now).
I just want to force admins, to setup usergroups for each user at its beeing created within the form.
This will work:
Pr\UserBundle\Entity\User:
properties:
locations:
- NotBlank:
message: { "You must select a location" }

Doctrine 2. How to force entity from proxy

I have 3 entities:
/**
* #ORM\Entity
* #ORM\Table(name="table_a")
*/
class A
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* ORM\OneToMany(targetEntity="B", mappedBy="entityA")
*/
protected $entitiesB;
/**
* ORM\OneToMany(targetEntity="C", mappedBy="entityA")
*/
protected $entitiesC;
/**
* #ORM\Column(type="string")
*/
protected $name;
}
/**
* #ORM\Entity
* #ORM\Table(name="table_b")
*/
class B
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* ORM\ManyToOne(targetEntity="A", inversedBy="entitiesB")
*/
protected $entityA;
/**
* #ORM\Column(type="date")
*/
protected $date;
}
/**
* #ORM\Entity
* #ORM\Table(name="table_c")
*/
class C
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* ORM\ManyToOne(targetEntity="A", inversedBy="entitiesC")
*/
protected $entityA;
/**
* #ORM\Column(type="string")
*/
protected $description;
}
And I have the following situation:
$eB = $repositoryB->find(1);
$eA = $eB->getEntityA(); // $eA will be a proxy
$eC = new C();
$eC->setDescription('XXXXXXXXXX')
->setEntityA($eA);
This will generate an error because $eA is a proxy not an entity. Even if I try:
$eB = $repositoryB->find(1);
$eA = $repositoryA->find(1);
$eC = new C();
$eC->setDescription('XXXXXXXXXX')
->setEntityA($eA);
Will still get an error because once you have fetched a B entity it will automatically fetch a proxy of A entity. And when you try to fetch the A entity with the same identifier as the proxy Doctrine will return the proxy object from the Identity Map because you can not have two objects (one proxy and one Entity) for the same db record.
So is there a way to force retrieving an entity from its proxy? Or another way to set an association, by id not by entity?

Resources