Many-to-Many Relations using non-"id" Primary Key in Doctrine - doctrine

So I'd like to create two entities and make a many-to-many reference. I would love to make this association using a string primary key on one table. This seems to be really hard, at least it took me pretty much time trying without any results yet.
This is my approach:
First entity:
namespace Project\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="User")
*/
class User
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $Id;
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="Users")
* #ORM\JoinTable(name="role_user",
* joinColumns={#ORM\JoinColumn(name="User_Id", referencedColumnName="Id")},
* inverseJoinColumns={#ORM\JoinColumn(name="Role_Name", referencedColumnName="Name")}
* )
*/
private $Roles;
}
And the second:
namespace Project\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="Role")
*/
class Role
{
/**
* #ORM\Id
* #ORM\Column(type="string", length=256)
*/
private $Name;
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="Roles")
* #ORM\JoinTable(name="role_user",
* joinColumns={#ORM\JoinColumn(name="Role_Name", referencedColumnName="Name")},
* inverseJoinColumns={#ORM\JoinColumn(name="User_Id", referencedColumnName="Id")}
* )
*/
private $Users;
}
Output of ./app/console doctrine:schema:validate:
[Mapping] FAIL - The entity-class 'Project\AdminBundle\Entity\User' mapping is invalid:
* The referenced column name 'Id' has to be a primary key column on the target entity class 'Project\AdminBundle\Entity\User'.
* The referenced column name 'Id' has to be a primary key column on the target entity class 'Project\AdminBundle\Entity\Role'.
What do I miss?

Attention upper/lowercase! Doctrine generates its columns as lowercase per default. This solves the issue:
* joinColumns={#ORM\JoinColumn(name="User_Id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="Role_Name", referencedColumnName="name")}
This it over-complicated; it's enough to put this into the Role entity:
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="Roles")
* #ORM\JoinColumn(name="role_user", referencedColumnName="name")
*/
private $Users;
I only thought about this when writing the question.. what a good method to think about the problem again from scratch.
Cheers

Related

Symfony 5 - Doctrine - Index not created in custom ManyToOne relation

I've created two Entity, and i try to create a custom relation between them, without using the default syntax.
See :
/**
* #ORM\Entity(repositoryClass=LandRepository::class)
* #ORM\HasLifecycleCallbacks()
*/
class Land
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $libelle;
/**
* #ORM\Column(type="integer")
*/
private $uid;
/**
* #ORM\OneToMany(targetEntity=Ride::class, mappedBy="uidparent")
*/
private $rides;
}
/**
* #ORM\Entity(repositoryClass=RideRepository::class)
* #ORM\HasLifecycleCallbacks()
*/
class Ride
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $libelle;
/**
* #ORM\ManyToOne(targetEntity=Land::class, inversedBy="rides")
* #ORM\JoinColumn(name="uidparent", referencedColumnName="uid")
*/
private $uidparent;
}
Tables are correctly created, but the last instruction have an error.
In MySQL, i made some test, and i need to create an index on "uid" column in "land" table.
So, i changed the header of my class "Land" to force the index
/**
* #ORM\Entity(repositoryClass=LandRepository::class)
* #ORM\Table(name="land",indexes={#ORM\Index(columns={"uid"})})
* #ORM\HasLifecycleCallbacks()
*/
class Land
{
/ ... /
}
I don't understand why i need to specify this index creation.
I hope to have something like this :
(PS : I know i can use the classic syntax (using in my entity Ride a column auto named "land_id") but I want to understand and master this alternative syntax to manage future complex entities and associations)
Thanks !
Need to manually specify in Entity header annotation :
#ORM\Table(name="land",indexes={#ORM\Index(columns={"uid"})})

Symfony ManyToMany relation add constraint without entity

I have ManyToMany relation, without entity and I want add constraint for ManyToMany table, example for uniq fields, how it's done ?
My constrait example for entity
* #ORM\Table(name="courses",
* uniqueConstraints={
* #UniqueConstraint(name="unique",
* columns={"courses_id", "courses_of_study_id"})
* }
* )
*/
class Courses
But this is approcah for entity/ In my case I don't need create entity, just relation ManyToMany in both side. This relation create table in database with courses_courses_of_study with courses_id and courses_of_study_id and I want add to it uniqueConstraints
may entity
class Courses
{
use TraitTimestampable;
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var ArrayCollection|CoursesOfStudy[]
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\CoursesOfStudy", inversedBy="courses", cascade={"persist", "remove"})
*/
private $coursesOfStudy;
class CoursesOfStudy
{
use TraitTimestampable;
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var ArrayCollection|Courses[]
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Courses", mappedBy="coursesOfStudy", cascade={"persist"})
*/
private $courses;
I try added this uniqueConstraints to entity Courses, but after migration diff I have error
[Doctrine\DBAL\Schema\SchemaException]
There is no column with name 'courses_id' on table 'courses'.
when I try
* #ORM\Table(name="courses")
* #ORM\Table(name="courses_courses_of_study",
* uniqueConstraints={
* #UniqueConstraint(name="unique",
* columns={"courses_id", "courses_of_study_id"})
* }
* )
*/
class Courses
have error
[Doctrine\DBAL\Schema\SchemaException]
The table with name 'project.courses_courses_of_study' already exists.

Symfony2 - UniqueEntity no action

I have an entity named Test with two fields: id and name.
I would like to have the name as unique.
What I did:
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
...
/**
* Company\AppBundle\Entity\Test
*
* #ORM\Table(name="test")
* #UniqueEntity("name")
* #ORM\Entity(repositoryClass="Company\AppBundle\Entity\TestRepository")
*
*/
class Test
{
....
/**
* #var string$name
*
* #ORM\Column(name="name", type="string", length=200, nullable=false, unique=true)
*/
private $name;
....
In my controller, I am using:
if ($form->isValid()) {
....
But the validation goes through. Am I missing something?
The unique annotation is for doctrine, it passes it to the database level and the error gets thrown from there. It will not know that the entity exists until you try to save it. To do the checks in php you have to query for the unique attribute yourself and check if it exists...

Assert unique validation in Sonata Admin

I'm using Symfony 2.1 for a project. I use SonataAdminBundle for administration usage.
i want to add an assert to my slug property in my admin class.. how can i do this?
in my entity i had set the assertion but it seems that it doesn't work here :(
related codes:
the entity :
/*
* #ORM\Table(name="default_doctor_specialty")
* #UniqueEntity("uniqueSlugName")
* #ORM\Entity
*/
class Test {
//..
/**
* #var string
* #Gedmo\Slug(fields={"name"},unique=false)
*
* #ORM\Column(name="unique_slug_name", type="string", length=255, nullable=false , unique=true)
*/
private $uniqueSlugName;
}
in admin class:
class TestAdmin extends Admin {
protected $formOptions = array(
'validation_groups' => 'Default'
);
//...
}
why the default validation doesn't work???
& even if doesn't work like this how can i set the unique validation inside admin class ???
thanks for your answers :)
finally i defined a validation group for my entity:
use Symfony\Bridge\Doctrine\Validator\Constraints as DoctrineAssert;
/*
* #ORM\Table(name="default_doctor_specialty")
* #DoctrineAssert\UniqueEntity(fields="uniqueSlugName", message="A Speciality with same slug already exists", groups={"test"})
* #ORM\Entity
*/
class Test {
//..
/**
* #var string
* #Gedmo\Slug(fields={"name"},unique=false)
*
* #ORM\Column(name="unique_slug_name", type="string", length=255, nullable=false , unique=true)
*/
private $uniqueSlugName;
}
and in admin class i used test validation group instead of default!
thanks to AHWEBDEV on github!
From this link
This is the full exemple , it depend on your symfony and sonata version.
// src/AppBundle/Entity/Service.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity
* #UniqueEntity(
* fields={"host", "port"},
* errorPath="port",
* message="This port is already in use on that host."
* )
*/
class Service
{
/**
* #ORM\ManyToOne(targetEntity="Host")
*/
public $host;
/**
* #ORM\Column(type="integer")
*/
public $port;
}
I prefer not to mess my entities with hundreds of lines of such low level details like validation. One can define validation rules inside the Admin class. Usually the validation rules are different for admins than for clients as well.
final class TestAdmin
{
// … skipped for brevity
public function validate(ErrorElement $errorElement, $object)
{
$errorElement->addConstraint(new UniqueEntity(['fields' => ['slug']]));
}
}

Symfony2 validation exception with unique constraint

I have an entity "Movie" which has a unique constraint through doctrine annotation. Based on the movie entity I have auto generated a CRUD layer. When I now try to add a new movie I get the following exception:
Only field names mapped by Doctrine can be validated for uniqueness.
When the constraint is removed everything works fine. Do somebody has an idea where the problem lays and how I can resolve it?
My guessing is the entity, because it is new, is not sync with the EntityManager and therefore could not check the constraint. Am I'close?
I'm using Symfony 2.0.1 with Doctrine 2.1.1, MySQL as Database.
Thanks,
-lony
The "Movie" Entity:
/**
* #ORM\Table()
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({"movie" = "Movie", "series" = "Series"})
*
* #DoctrineAssert\UniqueEntity("title_orginal")
*/
class Movie {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $titleOrginal
*
* #ORM\Column(name="title_orginal", type="string", length=255, unique="true")
*/
private $titleOrginal;
..
Your syntax is wrong.
Use this:
#DoctrineAssert\UniqueEntity(fields={"title_orginal"})
instead of
#DoctrineAssert\UniqueEntity("title_orginal")
You can then customize the violation message like this :
#DoctrineAssert\UniqueEntity(fields={"title_orginal", message="my.custom.message"})
and translate this message by using a validators.xliff file (it must be named like that).
I tell you this because I struggled the other day with it and was obliged to debug to find about this validators.xliff naming convention.
I think there is a small typo:
#DoctrineAssert\UniqueEntity(fields={"title_orginal", message="my.custom.message"})
should be:
#DoctrineAssert\UniqueEntity(fields={"title_orginal"}, message="my.custom.message")
and for several fields
#DoctrineAssert\UniqueEntity(fields={"title_orginal", "field2"}, message="my.custom.message")

Resources