Doctrine MappedSuperclass and unique constraints - php

I have a scenario where I need to use the MappedSuperclass functionality of Doctrine (using Symfony2), and also create a unique constraint on some superclass columns. Let's say:
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\MappedSuperclass
*/
class Base
{
/**
* #ORM\Column(type="integer", nullable=false)
*/
private $someColumn;
}
/**
* #ORM\Entity
* #ORM\Table(uniqueConstraints={#ORM\UniqueConstraint(name="column_idx", columns={"someColumn"})})
*/
class Concrete extends Base
{
}
The problem is at processing of #ORM\Table annotation during schema generation:
[Doctrine\DBAL\Schema\SchemaException]
There is no column with name 'someColumn' on table 'Concrete'.
Is there a way to define a unique constraint of a mapped superclass?

Since the answer author didn't post the answer himself, let me quote him:
Try to use protected instead of private for entity field. You should always use protected or public for entity fields

Related

Symfony Doctrine ORM not a valid entity or mapped super class

I'm just making a new Entity as usual, but something goes wrong and console report this error and I couldn't generate the entity setter/getter:
[Doctrine\ORM\Mapping\MappingException]
Class "AppBundle\Entity\Admin_Actions" is not a valid entity or mapped super class.
Here is my Entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="admin_actions")
*/
class Admin_Actions
{
/**
* #ORM\Column(name="id",type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="uid",type="string",length=100)
*/
private $uid;
/**
* #ORM\Column(name="type",type="integer")
*/
private $type;
/**
* #ORM\Column(name="linedate",type="datetime")
*/
private $linedate;
}
If I do doctrine:mapping:info:
[Exception]
You do not have any mapped Doctrine ORM entities according to the current configuration. If you have
entities or mapping files you should check your mapping configuration for errors.
I've just waste an hour trying to investigate the problem and I've already tried to rewrite it from new but I'm missing something. What's wrong with this?
May be datetime field has same name as function/implementation in doctrine, I have got same mistake by naming a table "condition" which may be condition function in MySql query

Symfony2: Custom identifier in Sonata entities

I have an entity with a custom id (i.e. UUID) generated on __construct function.
namespace AppBundle\Entity;
use Rhumsaa\Uuid\Uuid;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Person
{
/**
* #ORM\Id
* #ORM\Column(type="string")
*/
private $id;
/**
* #ORM\Column(type="string")
*/
private $name;
public function __construct()
{
$this->id = Uuid::uuid4()->toString();
}
This entity is used in sonata and also in other part of the project. I need this entity to have id before persisting and flushing it, so I can not use a an auto-increment.
So, the problem is sonata don't let me create entities because it takes the create option as and edit on executing because that entity already has an id, but this entity does not exists at this moment, so it fails.
The problem isn't the library for generating UUID, any value for 'id' fails.
Anyone know how to solve it? Another similar approach to solve the problem?
You shouldn't set your id in the constructor, but rather use the prePersist Doctrine event to alter your entity before persisting it for the first time.
You may use annotations to do so, see the Doctrine Documentation on prePersist.
The issue with setting the id in the constructor is that you may override it when you're retrieving it from the database, in which case it will be incorrect.

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.

Symfony2 / Doctrine mapped superclass in the middle of class table inheritance

I currently have a model structure as follows:
/**
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="related_type", type="string")
* #ORM\DiscriminatorMap({"type_one"="TypeOne", "type_two"="TypeTwo"})
*/
abstract class BaseEntity {
... (all the usual stuff, IDs, etc)
/**
* #ORM\OneToMany(targetEntity="Comment", mappedBy="baseEntity")
*/
private $comments;
}
/**
* #ORM\Entity
*/
class TypeOne extends BaseEntity {
/**
* #ORM\Column(type="string")
*/
private $name;
/**
* #ORM\Column(type="string")
*/
private $description;
}
/**
* #ORM\Entity
*/
class TypeTwo extends BaseEntity {
/**
* #ORM\Column(type="string")
*/
private $name;
/**
* #ORM\Column(type="string")
*/
private $description;
}
/**
* #ORM\Entity
*/
class Comment {
... (all the usual stuff, IDs, etc)
/**
* #ORM\ManyToOne(targetEntity="BaseEntity", inversedBy="comments")
*/
private $baseEntity;
}
The idea here is to be able to tie a comment to any of the other tables. This all seems to be working ok so far (granted, I'm still exploring design options so there could be a better way to do this...), but the one thing I've noticed is that the subclasses have some common fields that I'd like to move into a common parent class. I don't want to move them up into the BaseEntity as there will be other objects that are children of BaseEntity, but that won't have those fields.
I've considered creating a MappedSuperclass parent class in the middle, like so:
/**
* #ORM\MappedSuperclass
*/
abstract class Common extends BaseEntity {
/**
* #ORM\Column(type="string")
*/
private $name;
/**
* #ORM\Column(type="string")
*/
private $description;
}
/**
* #ORM\Entity
*/
class TypeOne extends Common {}
/**
* #ORM\Entity
*/
class TypeTwo extends Common {}
I figured this would work, but the doctrine database schema generator is complaining that I can't have a OneToMany mapping on a MappedSuperclass. I didn't expect this to be a problem as the OneToMany mapping is still between the root BaseEntity and the Comment table. Is there a different structure I should be using, or other way to make these fields common without adding them on the BaseEntity?
From the Docs:
A mapped superclass is an abstract or concrete class that provides
persistent entity state and mapping information for its subclasses,
but which is not itself an entity. Typically, the purpose of such a
mapped superclass is to define state and mapping information that is
common to multiple entity classes.
That said, how can you associate one entity with one that is not?
More from the docs:
A mapped superclass cannot be an entity, it is not query-able and
persistent relationships defined by a mapped superclass must be
unidirectional (with an owning side only). This means that One-To-Many
assocations are not possible on a mapped superclass at all.
Furthermore Many-To-Many associations are only possible if the mapped
superclass is only used in exactly one entity at the moment. For
further support of inheritance, the single or joined table inheritance
features have to be used.
Source: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html
Update
Because your MappedSuperClass extends BaseEntity it also inherits the BaseEntity's associations, as if it were its own. So you effectively DO have a OneToMany on a MappedSuperClass.
To get around it, well, you'd need to modify/extend doctrine to work the way you want.
As far as native functionality goes you have two options:
Class Table Inheritance
You Common class and the resulting DB representation would have the common fields and child classes will now only have the fields specific to themselves. Unfortunately this may be a misrepresentation of your data if you are simply trying to group common fields for the sake of grouping them.
Make Common an Entity
It appears that all a Mapped Super Class is is an Entity that isn't represented in the DB. So, make common a Entity instead. The downside is that you'll end up with a DB table, but you could just delete that.
I recommend that you take a second look at your data and ensure that you are only grouping fields if they are common in both name and purpose. For example, a ComputerBox, a ShoeBox, a Man, and a Woman may all have the "height" property but in that case I wouldn't suggest have a Common class with a "height" property that they all inherit from. Instead, I would have a Box with fields common to ComputerBox and ShoeBox and I'd have a Person with fields common to Man and Woman. In that situation Class Table Inheritance or single table if you prefer would work perfectly.
If your data follows that example go with Single Table or Class Table Inheritance. If not, I might advise not grouping the fields.

Symfony 2 + Doctrine 2 + inheritance

I'm searching for a solution for the following problem with a database inheritance using Doctrine 2 built in Symfony 2 framework. This is what I want to do...
I want to create two tables (UredniHodiny, KonzultacniHodiny) with the same interface as the abstract class Hodiny. This is how I'm trying to do it
<?php
// src/CvutPWT/ImportBundle/Entity/Hodiny.php
namespace CvutPWT\ImportBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\MappedSuperclass
*/
abstract class Hodiny
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Osoba")
*/
protected $osoba;
/**
* #ORM\ManyToOne(targetEntity="Mistnost")
*/
protected $mistnost;
/**
* #ORM\Column(type="datetime")
*/
protected $zacatek;
/**
* #ORM\Column(type="datetime")
*/
protected $konec;
}
<?php
// src/CvutPWT/ImportBundle/Entity/KonzultacniHodiny.php
namespace CvutPWT\ImportBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="konzultacnihodiny")
*/
class KonzultacniHodiny extends Hodiny
{
}
<?php
// src/CvutPWT/ImportBundle/Entity/UredniHodiny.php
namespace CvutPWT\ImportBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="urednihodiny")
*/
class UredniHodiny extends Hodiny
{
}
Now when I run php app/console doctrine:generate:entities CvutPWTImportBundle Symfony generates all variables (more precisely columns) from class Hodiny as private variables to both child classes. Now when I'm trying to create those tables with app/console doctrine:schema:update --force I'm getting errors that $id must be protected or weaker. When I change this protection manually I am able to create tables but there is only one column (id). But this is not what I was hoping for. Can somebody give me any advice what I'm doing wrong?
This is not table inheritance. Mapped super classes are just mapping inheritance.
The tables corresponding to your final entities will not be relied together in any way.
If you want real table inheritance (single table or joined table), use this: http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/inheritance-mapping.html#single-table-inheritance
If you still want to use mapped super classes, then you will have to put the #ORM\Id definition in both final classes. You can not put ids in mapped super classes.

Categories