Doctrine ManyToOne w/o a primary ID key - symfony

I'm trying to setup an FK between 2 entities in my DB. The parent table has a ManyToOne relationship to a child table. I can not join these tables using a normal parent_id => id FK due to how the child table is populated by external processes (the parent never knows the primary ID of the child).
Doctrine accepts the entities as shown below but Mysql fails when trying to add the FK to the table with the following error.
ALTER TABLE parent_tbl ADD CONSTRAINT FK_1172A832F85E0677 FOREIGN KEY (username) REFERENCES child_tbl (username);
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`dmname`.`#sql-f16_d8acf`, CONSTRAINT `FK_1172A832F85E0677` FOREIGN KEY (`username`) REFERENCES `child_tbl` (`username`))
class Parent
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Child")
* #ORM\JoinColumn(name="username", referencedColumnName="username", nullable=false)
*/
protected $username;
// ...
}
class Child
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*
* #var integer
*/
protected $id;
/**
* #ORM\Column(type="string", unique=true)
*
* #var string
*/
protected $username;
// ....
}

From the Doctrine limitations and known issues :
It is not possible to use join columns pointing to non-primary keys.
Doctrine will think these are the primary keys and create lazy-loading proxies with the data, which can lead to unexpected results.
Doctrine can for performance reasons not validate the correctness of this settings at runtime but only through the Validate Schema command.
So, if you run the doctrine:schema:validate command, you should get something like :
[Mapping] FAIL - The entity-class Parent mapping is invalid:
* The referenced column name 'username' has to be a primary key column on the target entity class Child.
I hope you find a workaround to keep your logic intact using a primary key as join column.

Related

Doctrine: authorize NULL in a foreign composite key

I have the following entity:
/**
* SeriesAuthorRole
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Blog\Bundle\CoreBundle\Entity\SeriesAuthorRoleRepository")
*/
class SeriesAuthorRole extends AuthorRoleAbstract
{
/**
* #var Series
*
* #ORM\ManyToOne(targetEntity="Blog\Bundle\CoreBundle\Entity\Series", inversedBy="authors")
* #ORM\JoinColumn(name="series", referencedColumnName="id", nullable=false)
* #ORM\Id
*/
private $series;
/**
* #var Author
*
* #ORM\ManyToOne(targetEntity="Blog\Bundle\CoreBundle\Entity\Author")
* #ORM\JoinColumn(name="author", referencedColumnName="id", nullable=false)
* #ORM\Id
*/
protected $author;
/**
* #var Role
*
* #todo Must be nullable
*
* #ORM\ManyToOne(targetEntity="Blog\Bundle\CoreBundle\Entity\Role")
* #ORM\JoinColumn(name="role", referencedColumnName="id", nullable=true)
* #ORM\Id
*/
protected $role;
// ... Getters, setters
}
The idea behind it is quite simple: We have author, role and series entities. A series can have several authors with various roles. A same author can fulfill multiple roles in a series.
Sometimes, we don't know exactly what was the role of the author. In this case, the NULL value will be used for the role, the NULL value standing for "I don't know".
I was taught not to use NULL in foreign composite keys unless it has meaning. Well, it has meaning here, and I know that this could be implemented without Doctrine. However, for now, Symfony 2 throws that error:
Entity of type Blog\Bundle\CoreBundle\Entity\BandAuthorRole is missing an assigned ID for field 'role'. The identifier generation strategy for this entity requires the ID field to be populated before EntityManager#persist() is called. If you want automatically generated identifiers instead you need to adjust the metadata mapping accordingly.
500 Internal Server Error - ORMException
So how can I authorize NULL values in foreign composite keys ? Is it possible at all with Doctrine ?
Your #JoinColumn annotation is correct with referencing to http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#annref-joincolumn
However,
Every entity with a composite key cannot use an id generator other
than “ASSIGNED”. That means the ID fields have to have their values
set before you call EntityManager#persist($entity).
http://docs.doctrine-project.org/en/2.0.x/tutorials/composite-primary-keys.html#general-considerations

Doctrine not found entity id

i have a problem mapping entities in Doctrine 2 (with Symfony2). Symfony says that error is in Perfil entity, but the Doctrine section profiler says that the error is en Funcion entity.
This is the error (on the browser):
The column id must be mapped to a field in class
AppsManantiales\CommonBundle\Entity\Perfil since it is referenced by a
join column of another class.
But, in the profiler:
AppsManantiales\CommonBundle\Entity\Funcion: The referenced column
name 'id' has to be a primary key column on the target entity class
'AppsManantiales\CommonBundle\Entity\Funcion'. The referenced column
name 'id' has to be a primary key column on the target entity class
'AppsManantiales\CommonBundle\Entity\Perfil'.
This is the relation er:
The Perfil entity, basically is:
class Perfil implements RoleInterface{
/**
* #ORM\Id
* #ORM\Column(type="integer", length=10)
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $idperfil;
/**
* #ORM\ManyToMany(targetEntity="Funcion", mappedBy="perfiles")
* #ORM\JoinTable(name="perfil_funcion",
* joinColumns={#ORM\JoinColumn(name="perfil", referencedColumnName="idperfil")},
* inverseJoinColumns={#ORM\JoinColumn(name="funcion", referencedColumnName="idfuncion")}
* )
*/
protected $funciones;
// More code...
}
The Funcion entity basically is:
class Funcion {
/**
* #ORM\Id
* #ORM\Column(type="integer", length=11)
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $idfuncion;
/**
* #ORM\ManyToMany(targetEntity="Perfil", inversedBy="funciones")
*/
protected $perfiles;
// More code...
}
Any ideas ? Thanks !
I think by default Doctrine relies on the convention that all primary keys in entities are represented by an attribute called $id. Two solutions :
Rename your primary keys from $idperfil and $idfuncion to $id (which I recommend);
Explicit the join criterion on your Funcion object on the $perfiles property:
* #ORM\JoinTable(name="perfil_function"),
* joinColumns={#ORM\JoinColumn(name="funcion", referencedColumnName="idfuncion")},
* inverseJoinColumns={#ORM\JoinColumn(name="perfil", referencedColumnName="idperfil")}
* )

Foreign key annotation

I have a quite simple 3 table schema, but I couldn't find the solution for annotating the entity in Symfony2.
This are my tables:
user:
id, name, joined
skill:
id, name, created
user_skills:
user, skill
As you understand the last table can contain several rows for each customer and for each skill, but the there can not be duplicate rows with same user and skill.
How do I annotate the vars Entity of: user_skills
because it is not OneToMany, or ManyToMany, its a simple foreign key for other table.
Sorry, my solution is right if you need all the users skills in Skill entity for later's DB queries. I guess this solution will be fine for you:
class UserSkills
{
/**
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity=\"your route"\Entity\User")
* #ORM\JoinColumn(name="user_id", referencedColumnName="user_id")
*/
private $user;
/**
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity=\"your route"\Entity\Skill")
* #ORM\JoinColumn(name="skill_id", referencedColumnName="skill_id")
*/
private $skill;

Symfony2 ORM FK Composite

I'm new with Symfony2 and Doctrine 2 ORM, then i got a problem with composite FK association.
While the association OneToMany-ManyToOne by a single auto-increment ID works good, i got many troubles with the same kind of association but through a composite PK.
What i would like to do is an entity-structure that mirrors the below shema.
http://imageshack.us/photo/my-images/9/z07m.jpg (Sorry but I cannot insert images into the post)
where
prd_product_data.product_id
prd_product_data.language_id
make up together the PK of 'prd_product_data'
and
prd_product_image.pdoduct_id
prd_product_image.language_id
make up the FK linked to the table 'prd_product_data'.
These below
prd_product_image.pdoduct_id
prd_product_image.language_id
prd_product_image.file_image_id
make up, all together, the PK of 'prd_product_image'.
I read that Doctrine 2.1 and up, supports natively the composed PK - FK association but there are very few examples. Moreover, surfing on the web, i found just scattered fragments of similar situations but nothing of useful.
The main issue now, is that i'm unable to create one unique object for the two ids, like such an unique id.
For example...
Snapshot for the Entity ProductData where "product and language should make up the same object:
/**
*
* #ORM\OneToMany(targetEntity="ProductImage", mappedBy="product")
*/
protected $products_Pimgs;
/**
*
* #ORM\OneToMany(targetEntity="ProductImage", mappedBy="language")
*/
protected $products_Limgs;
Snapshot for the Entity ProductImage where *product_id* and *language_id* made up the FK for the entity ProductData:
/**
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="ProductData", inversedBy="products_Pimgs")
* #ORM\JoinColumn(name="product_id", referencedColumnName="product_id")
*/
protected $product;
/**
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="ProductData", inversedBy="products_Limgs")
* #ORM\JoinColumn(name="language_id", referencedColumnName="language_id")
*/
protected $language;
/**
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="FileImage", inversedBy="files_images")
* #ORM\JoinColumn(name="file_image_id", referencedColumnName="id")
*/
protected $file_image;
From these snapshots it's evident that those keys are working separately.
In the end, this is my doctrine version, taken from the composer:
"doctrine/orm": ">=2.2.3,<2.4-dev",
How can i create an unique reference with two objects with ORM?
Read the documentation chapter Composite Primary Keys.
/**
* #ORM\Id
* #ORM\OneToMany(targetEntity="ProductImage", mappedBy="product")
*/
protected $products_Pimgs;
/**
* #ORM\Id
* #ORM\OneToMany(targetEntity="ProductImage", mappedBy="language")
*/
protected $products_Limgs;

doctrine2: undefined index - many-to-one with non default referencedColumnName does not persist entity

I'm using Symfony 2.1.2.
I have two entities and define a [many-to-one (bidirectional)] (1) association between them. I don't want to use the primary key for the foreign key (referencedColumnName). I want to use another integer unique column: customer_no
/**
* #ORM\Entity
* #ORM\Table(name="t_myuser")
*/
class MyUser extends BaseEntity // provides an id (pk)
{
/**
* #ORM\ManyToOne(targetEntity="Customer", inversedBy="user")
* #ORM\JoinColumn(name="customer_no", referencedColumnName="customer_no", nullable=false)
*/
public $customer;
}
/**
* #ORM\Entity
* #ORM\Table(name="t_customer")
*/
class Customer extends BaseEntity // provides an id (pk)
{
/**
* #ORM\Column(type="integer", unique=true, nullable=false)
*/
public $customer_no;
/**
* #ORM\OneToMany(targetEntity="MyUser", mappedBy="customer")
*/
public $user;
}
When I try to persist a MyUser entity with an Customer entity, I get this error:
Notice: Undefined index: customer_no in ...\vendor\doctrine\orm\lib\Doctrine\ORM\Persisters\BasicEntityPersister.php line 608
The schema on the db looks fine, these should be the important sql schema definitions:
CREATE UNIQUE INDEX UNIQ_B4905AC83CDDA96E ON t_customer (customer_no);
CREATE INDEX IDX_BB041B3B3CDDA96E ON t_myuser (customer_no);
ALTER TABLE t_myuser ADD CONSTRAINT FK_BB041B3B3CDDA96E FOREIGN KEY (customer_no)
REFERENCES t_customer (customer_no) NOT DEFERRABLE INITIALLY IMMEDIATE;
So there is definitely an index for customer_no
//update:
I fix the inversedBy and mappedBy stuff, but this is not the problem.
(1) : http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-bidirectional
#m2mdas:
Yes you're right, I thought it's possible because JPA (which has influence to doctrine) has this feature. The attribute referencedColumnName only for the case when your property does not match the table column.
Whatever, I found a solution by patching the BasicEntityPersister.php, see here the gist on github: https://gist.github.com/3800132
the solution is to add the property/field name and value for the mapping column. This information is already there but not bound to the right place. It have to be added to the $newValId arrray this way:
$fieldName = $targetClass->getFieldName($targetColumn);
$newValId[$fieldName] = $targetClass->getFieldValue($newVal, $fieldName);
It only works for ManyToOne reference. ManyToMany doesn't work.
For ManyToOne I test it with already existing entities. You can test it, too:
change the doctrine annotation in tests/Doctrine/Tests/Models/Legacy/LegacyArticle.php
from
#JoinColumn(name="iUserId", referencedColumnName="iUserId")
to
#JoinColumn(name="username", referencedColumnName="sUsername")

Resources