Symfony/Doctrine class table inheritance and foreign key as primary key - php

I am currently designing a web application with Symfony 2.5 (and Doctrine 2.4.2) that has to be flexible to easily plug in new modules/bundles.
So I have an entity (let say A) that has two one-to-one associations with abstract classes (B and C). The future modules will implement one of the two abstract classes. Since the associations are one-to-one, I made them the ID of the abstract classes to ease the access to them when we know the ID of an instance of A
So here is what the code looks like:
class A:
<?php
namespace Me\TestBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table()
* #ORM\Entity
*/
class A
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="B", cascade={"all"}, mappedBy="a")
* #ORM\JoinColumn(name="id", referencedColumnName="a_id")
*/
private $b;
/**
* #ORM\OneToOne(targetEntity="C", cascade={"all"}, mappedBy="a")
* #ORM\JoinColumn(name="id", referencedColumnName="a_id")
*/
private $c;
}
class B:
<?php
namespace Me\TestBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table()
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
*/
abstract class B
{
/**
* #ORM\Id
* #ORM\OneToOne(targetEntity="A", inversedBy="b")
* #ORM\JoinColumn(name="a_id", referencedColumnName="id")
*/
private $a;
}
I will not post the code of the class C since it is the same as class B.
In my point of view, it seems all good. Even for the mapping verification command.
Indeed, when I execute php app/console doctrine:schema:validate, it tells me my schema is valid. However, this command then try to compare my schema to the schema in the database and it just fails at that point. php app/console doctrine:schema:update --dump-sql fails in the exact same way. So it is pretty embarrassing since it tells me my schema is valid but it cannot use it properly.
The error:
[ErrorException]
Warning: array_keys() expects parameter 1 to be array, null given in /vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php line 95
The error appears as soon as I add the InheritanceType and DiscriminatorColumn annotation to the classes B and C. Thing is that it tells me my schema is valid.
So does anyone have any clue if I am doing something wrong? Or is it definitely a bug in Doctrine? Do you have any other idea that would bring at least as much flexibility as my current solution?
Elioty
EDIT: I changed the owning side to be the abstract classes B and C since, accordingly to the doc, the owning side is the one with the foreign key and must use inversedBy attribute. Even with these changes, my schema is still valid and the same error still occurs.
EDIT2: If I create another field in B (and C) to hold the identity of the entity instead of the one-to-one association, the error disappears but it is no more like my valid schema.
EDIT3: I had a chat with a member of Doctrine's development team and (s)he told me it definitely looks like a bug. Bug report here.

First of all, the command php app/console doctrine:schema:validate checks the validity of the current schema. The current schema is the one generated by your last called to the command php app/console doctrine:schema:update --force
On the other hand the command php app/console doctrine:schema:update --dump-sql does not execute an actual update to the schema, though it may find some errors some others will not be come up...
I dont understand class B. The identity of this (abstract) class is a OneToOne relationship? Anyway, the thing is that Doctine will ignore the #ORD\Id if you don't add inheritanceType definitions. When you add this inheritanceType definition Doctrine will make attribute 'a' the primary key of B. Also it will create another attribute named 'a_id' which will serve as a foreign key for future subclasses (extends of B). But...relationships are not inherited.
Hope this helps...

Related

The referenced column name has to be a primary key column on the target entity class

everyone. Happy new year! Today I am having problems trying to resolving this problem:
Could not resolve type of column "id_usuario" of class "FacturadorVirtual\Modelos\Seguridad\Usuario"
For some, this error is only present in linux enviroment. In windows, no problem. What the problem is exactly? Looks like PlanAdquirido is not finding the column id_usuario through the relationship $adquiridoPor. Again: this error is only triggered in linux enviroment.
Usuario entity:
<?php
namespace FacturadorVirtual\Modelos\Seguridad;
use Illuminate\Contracts\Auth\Authenticatable;
/**
* #entity
* #table(name="usuarios")
*/
class Usuario implements Authenticatable
{
/**
* #id
* #var integer
* #column(type="integer", name="id_usuario")
* #generatedValue(strategy="AUTO")
*/
protected $id;
}
PlanAdquirido entity:
<?php
namespace FacturadorVirtual\Modelos\Planes;
use Doctrine\Common\Collections\ArrayCollection;
use FacturadorVirtual\Modelos\Seguridad\Usuario;
/**
* #entity
* #table(name="planes_adquiridos")
*/
class PlanAdquirido
{
/**
* #id
* #var integer
* #column(type="integer", name="id_plan")
* #GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var Usuario
* #ManyToOne(targetEntity="FacturadorVirtual\Modelos\Seguridad\Usuario")
* #JoinColumn(name="adquirido_por", referencedColumnName="id_usuario")
*/
protected $adquiridoPor;
}
If I run "vendor/bin/doctrine.bat" orm:validate-schema in windows I get:
Mapping
-------
[OK] The mapping files are correct.
But in linux I get:
Mapping
-------
[FAIL] The entity-class FacturadorVirtual\Modelos\Planes\PlanAdquirido mapping is invalid:
* The referenced column name 'id_plan' has to be a primary key column on the target entity class 'FacturadorVirtual\Modelos\Planes\PlanAdquirido'.
* The referenced column name 'id_usuario' has to be a primary key column on the target entity class 'FacturadorVirtual\Modelos\Seguridad\Usuario'.
Column name id_usuario referenced for relation from FacturadorVirtual\Modelos\Planes\PlanAdquirido towards FacturadorVirtual\Modelos\Seguridad\Usuario does not exist.
I do not know if I am missing something, but have I have two days trying to figure out why is not working in linux (where production will reside).
Tables and column's name are all in lower case, using MySQL 5.7
Today I found the answer. The problem was redi's cache. Everytime the project was updated I ran the clear cache command:
"vendor/bin/doctrine" orm:clear-cache:metadata
I thought that this command already cleared the cache, but it did not. To clear the cache correcly just run:
$ redis-cli
> flushall
All works great now since then!

Symfony Annotations are not parsed

I have installed the FOSUserBundle and installed it as per its detailed installation guide (https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.md).
When i run php console doctrine:schema:update --force for the first time, it populates the users table with all of the default fields that the FOSUserBundle has defined.
Unfortunately it appears to be completely missing the fields which i have added to my user entity and i am wondering if its utilising the configuration file which is specified in the installation guide instead of using the annotations which are in the entity.
It also appears to be ignoring the other entities within the same Bundle.
namespace Acme\UserBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="users")
*/
class User extends BaseUser {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="integer")
* #ORM\ManyToOne(targetEntity="Bureau", mappedBy="id")
*/
protected $bureau;
public function __construct() {
parent::__construct();
}
}
This is my user entity, of which bureau is being completely ignored regardless if it has a relationship or not.
Edit
As per requested, please find below the orm config file. It's the default file as per the configuration.
I have suspected this to be the problem, but i wasnt sure if annotations and the config file could work together.
Acme\UserBundle\Entity\User:
type: entity
table: users
id:
id:
type: integer
generator:
strategy: AUTO
Edit 2
I have found that if i remove the orm configuration file that it all magically works again!!
So i would adjust my question for clarity.
Updated question
If an orm configuration file exists, are annotations ignored?
When you generate entities with the console, you are asked on the format, which is:
xml
yaml
annotations
Regardless of what you choose, there are no signifiers telling Doctrine which to use besides the fact one exists. In order, YAML takes priority over annotations, and so it should.

Table doesn't exist within FOSUserBundle

I'm currently developing an administration interface with FOSUserBundle, but I'm facing an annoying issue:
I've created an Operator class to hold FOSUserBundle's users, which is mapped in Symfony2 using Doctrine ORM. The first letter of Operator is uppercase (the rest of the tables in the website are also prefixed with capital letters, so I didn't want to break that convention).
The issue is that when I log in my administration interface, an error pops saying:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'dbname.operator' doesn't exist
Obviously, the query should use dbname.Operator, not dbname.operator.
Any help would be much appreciated.
Thanks!
Edit: attached the class definition of the Operator Entity:
/**
* #ORM\Entity
* #ORM\Table(name="Operator")
*/
class Operator extends BaseUser {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
}
Have you run
doctrine:schema:update --force
To create the database table?
You can set the database table name in your entity.
If you use annotations, your entity should start something like this:
/**
* Operator
*
* #ORM\Table(name="Operator")
*/
class Operator
{

Symfony2: Duplicate definition of column 'id' on entity in a field or discriminator column mapping

I'm having trouble using entity inheritance in Symfony2. Here are my two classes:
use Doctrine\ORM\Mapping as ORM;
/**
* #Orm\MappedSuperclass
*/
class Object
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
}
/**
* #Orm\MappedSuperclass
*/
class Book extends Object
{
}
When I run php app/console doctrine:schema:create I get the following error:
[Doctrine\ORM\Mapping\MappingException]
Duplicate definition of column 'id' on entity 'Name\SiteBundle\Entity\Book' in a field or discriminator column mapping.
What may be causing this?
Thanks :)
Update:
You are right I missed this. Now I'm using single table inheritance with both classes being entities:
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"object" = "Object", "book" = "Book"})
*/
But I still get the same error message.
Actually I found yml files in Resources/config/doctrine/, which were defining my entities, instead of just using annotations.
I removed these files and it's working now.
Thanks for your help !
I had same issue even after adding definitions to yml file. I was trying to add weight & max weight to a class and was getting:
Duplicate definition of column 'weight_value' on entity 'Model\ClientSuppliedProduct' in a field or discriminator column mapping.
Then I realized it requires columnPrefix to be different for similar types of fields and adding following in yml solved it for me:
`maxWeight:`
`class: Model\Weight`
`columnPrefix: max_weight_`
I had the same problem and error message but for me it was the other way around as #user2090861 said.
I had to remove the (unused)
use Doctrine\ORM\Mapping as ORM;
from my entity files, cause my real mapping comes from the orm.xml files.
I hope I can help with my answer many other people, cause this exception drove me crazy the last two days!
I ran into this in a different context - in my case, I had set up an entity for single-table inheritence using #ORM\DiscriminatorColumn, but had included the column in my class definition as well:
/**
* #ORM\Entity(repositoryClass="App\Repository\DirectoryObjectRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="kind", type="string")
*/
class DirectoryObject {
// ...
/**
* #ORM\Column(type="string", length=255)
*/
private $kind;
}
Removing the #ORM\Column definition of kind fixed this issue, as Doctrine defines it for me.
Sometimes it's impossible to remove extra config files, because theay are located in third party bundle and auto_mapping is enabled.
In this case you should disable undesirable mappings in app/config.yml
doctrine:
orm:
entity_managers:
default:
mappings:
SonataMediaBundle: { mapping: false }
Any entity must contain at least one field.
You must add at least one field in Book Entity
Example
/**
* #Orm\MappedSuperclass
*/
class Book extends Object
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
}
I had the same error message but I had made a different mistake:
Class B had an ID and extended Class A which also had an ID (protected, not private). So I had to remove the ID from Class B.

Doctrine 2 - Access level problems when using Class Table Inheritance

I'm trying to implent the Class Table Inheritance Doctrine 2 offers in my Symfony 2 project.
Let's say a have a Pizza class, Burito class and a MacAndCheese class which all inherit from a Food class.
The Food class has the following settings:
<?php
namespace Kitchen;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="food")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="dish", type="string")
* #ORM\DiscriminatorMap({"pizza" = "Pizza", "burito" = "Burito", "mac" => "MacAndCheese"})
*/
class Food {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
And the inherited classes have these settings (Pizza for example):
<?php
namespace Kitchen;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="food_pizza")
*/
class Pizza extends Food {
When running doctrine:schema:update --force from the Symfony 2 app/console I get an error about the access level of $id in the children of Food (Pizza for example), stating it must be protected or weaker. I haven't declared $id anywhere in the Pizza, since I reckoned it would be inherited from Food.
So I tried to declare $id, but that gives me an error, cause I can't redeclare $id.
I figure I need some kind of reference to $id from Food in Pizza, but the Doctrine 2 documentation didn't really give me a clear answer on what this would look like.
Hopefully you understand what I mean and can help me.
Apparently I should have investigated the code generated by doctrine:generate:entities a bit more. When I started my IDE this morning and seeing the code again, I noticed that it had 'copied' all of the inherited fields (like $id in Food, in the example above) to the children (Pizza, in the example above).
For some reason it decided to make these fields private. I manually changed the access level to protected in all of the classes and I tried to run doctrine:schema:update --force again: it worked!
So, as in many cases, the solution was a good night's rest! ;)
If someone comes up with a better solution and / or explanation for this problem, please do post it. I'd be more than happy to change the accepted answer.
Something to keep in mind:
Every Entity must have an identifier/primary key. You cannot generate
entities in an inheritance hierachy currently (beta) As a workaround
while generating methods for new entities, I moved away from project
inheritated entities and after generating I moved them back.
source
May be you should define the #ORM\DiscriminatorMap in a such way:
/**
*
..
* #ORM\DiscriminatorMap({"food" = "Food", "pizza" = "Pizza", "burito" = "Burito", "mac" => "MacAndCheese"})
*/
If you compare your code with the example from Doctrine site, you will see that they added parent entity to the DiscriminatorMap.

Categories