I am needing to make a formula to DiscriminatorMap in my class, because I have a lot of class, and I can't discrimine each one.
The discr can be the name of the class.
it's possible? (with annotation, xml or other)
/**
* #ORM\Entity
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"MidUpperArmCircumference" = MidUpperArmCircumference", "KneeHeight" = "KneeHeight"})
*/
thanks.
Look this link maybe it'll help you.
https://medium.com/#jasperkuperus/defining-discriminator-maps-at-child-level-in-doctrine-2-1cd2ded95ffb
I simply left out the DiscriminatorMap annotation and Doctrine automatically used the chield's class name as a discriminator:
/**
* #ORM\Entity()
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string")
*/
abstract class AbstractContent
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
}
/**
* #ORM\Entity()
*/
class Page extends AbstractContent
{
}
Now when I create a new Page() Doctrine creates an AbstractContentand a Page with an FK to the AbstractContent and sets the AbstractContent's type attribute to page.
This perfect as it let's you generate as many subclasses as you like even in other Bundles without your Superclass (in my case AbstractContent) needing to know about them.
But keep in mind that so far this isn't officially documented. Tested with Doctrine ORM 2.3.
This is an old question. Doctrine supports single table inheritance pretty well.
The below example is from official docs
<?php
namespace MyProject\Model;
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
*/
class Person
{
// ...
}
/**
* #Entity
*/
class Employee extends Person
{
// ...
}
Read more about it here
Related
I'm working with Symfony and MySQL and I'm trying to follow some convention across all my table, one of them is to keep each id's colmun name in the format id_tablename (see diagram). So i kept the id name generated by Symfony in the classes, but I want to replace each field in the database by id_product, id_tire, etc, ...
For that i'm using the Column annotation, e.g:
abstract class Product
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer", name="id_product")
*/
private $id;
// ...
}
And for each child class, I use AttributeOverride annotation as explained in the doc, like bellow
/**
* #ORM\Entity(repositoryClass=TireRepository::class)
* #ORM\AttributeOverrides(
* #ORM\AttributeOverride(
* name = "id",
* column=#ORM\Column(name="id_tire")
* )
* )
*/
class Tire extends Product
{
// ...
}
But when attempting a php bin/console make:migration I got the error The column type of attribute 'id' on class 'App\Entity\Tire' could not be changed.
Did I miss something ?
Edit: I tried to override another attribute ($name) with the following code that work:
/**
* #ORM\Entity(repositoryClass=RimRepository::class)
* #ORM\AttributeOverrides(
* #ORM\AttributeOverride(
* name = "name",
* column=#ORM\Column(name="name_rim")
* )
* )
*/
class Rim extends Product
{
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
// ...
}
But even by doing the same thing with $id attribute, I still have the same error message.
Seem like Doctrine have difficult to work with renamed fields too, when you have relations betweens classes. So for now I keep the default id name for each table in database, to continue working.
Please check correct example below.
Looks like you just missing type="integer" in AttributeOverride
use Doctrine\ORM\Mapping as ORM;
/**
* #MappedSuperclass
*/
abstract class Product
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer", name="id_product")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
*/
protected $name;
// ...
}
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\AttributeOverrides(
* #ORM\AttributeOverride(
* name = "id",
* column=#ORM\Column(name="id_tire", type="integer")
* )
* )
*/
class Tire extends Product
{
// ...
}
As result migration SQL will be similar to following
$this->addSql('CREATE TABLE tire (id_tire INT NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id_tire))');
It seem like it's a problem with how Doctrine works. As my system require many relations between entities, I didn't noticed it, but without relations, evrything works fine if they are correctly mapped. For exemple with:
Parent class
/**
* #ORM\Entity(repositoryClass=ProductRepository::class)
*/
class Product
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer", name="id_product")
*/
private $id;
// ...
}
Child classes
/**
* #ORM\Entity(repositoryClass=RimRepository::class)
* #ORM\AttributeOverrides(
* #ORM\AttributeOverride(
* name = "id",
* column=#ORM\Column(type="integer", name="id_rim")
* )
* )
*/
class Rim extends Product
{
/**
* #ORM\Column(type="integer")
*/
private $id;
// ...
}
/**
* #ORM\Entity(repositoryClass=TIreRepository::class)
* #ORM\AttributeOverrides(
* #ORM\AttributeOverride(
* name = "id",
* column=#ORM\Column(type="integer", name="id_tire")
* )
* )
*/
class Tire extends Product
{
/**
* #ORM\Column(type="integer")
*/
private $id;
// ...
}
Will generate those tables in database. But with relations at any level of the hiearchy like in this case, Doctrine will fail to retrive renamed column with annotations. Si in my case I had to keep the default id name across all tables in order to let Doctrine find what he excpect when making relations between tables.
I tried to remove all relation from child class and keep those from parent class, also the opposite, but Doctrine alwas still searching column id while looking for relation/contraints:
$this->addSql('ALTER TABLE picture ADD CONSTRAINT FK_16DB4F894584665A FOREIGN KEY (product_id) REFERENCES product (id)');
It's seem like impossible to do right now, with complex database structure.
I get unnecessary queries then entity has ManyToOne relationship with abstract class.
My classes structure:
/**
* #ORM\Entity
* #ORM\Table(name="tb_payment_info")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="integer")
* #ORM\DiscriminatorMap({
* "0" = "PaymentInfoPaypal",
* "1" = "PaymentInfoSkrill",
* })
*/
abstract class AbstractPaymentInfo
{
/**
* #ORM\Column(name="payment_info_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
}
/**
* #ORM\Entity
* #ORM\Table(name="tb_payment_info_paypal")
*/
class PaymentInfoPaypal extends AbstractPaymentInfo
{
}
/**
* #ORM\Entity
* #ORM\Table(name="tb_payment_info_skrill")
*/
class PaymentInfoSkrill extends AbstractPaymentInfo
{
}
My Payout class contains payment_info_id column from tb_payment_info table.
/**
* #ORM\Entity
* #ORM\Table(name="tb_payout")
*/
class Payout
{
/**
* #var AbstractPaymentInfo
*
* #ORM\ManyToOne(targetEntity="AbstractPaymentInfo")
* #ORM\JoinColumn(name="payment_info_id", referencedColumnName="payment_info_id")
*/
private $paymentInfo;
}
When I try to get any Payout entity, its paymentInfo initialize automatically. So:
$this->getEntityManager()->getRepository('TuoPayBundle:Payout')->find(255);
got 2 queries: first for Payout and second for its paymentInfo
$this->getEntityManager()->getRepository('TuoPayBundle:Payout')->findBy(['id'=>[255,256]]);
got 3 queries: first for Payout and second, third separate queries to init paymentInfo
How to achieve lazy load?
You cannot declare an abstract class in Doctrine 2 with #ORM\Entity notation. If you want to use abstract classes in your object model I suggest you check the documentation on Mapped Superclasses on how to do that correctly.
Most importantly you should declare the class with a special #ORM\MappedSuperClass annotation.
Keep in mind that Mapped superclasses come with restrictions. I quote:
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 associations 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.
I have many ads entities (MotorAds, RealestateAds, ElectronicsAds, ...) that share some attributes like title and description. In order to avoid redefining these attributes for each Ads entity, one can use the mapped superclass methods as follows:
<?php
/** #MappedSuperclass */
class MappedSuperclassAds{
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255, nullable=false)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="description", type="text", nullable=false)
*/
private $description;
}
Then, the inheritance will do the job.
Now, what is the problem? The problem is that each Ads entity is related to its entity that defines the list of users that added the ads to their favorites. To do that (the MotorsAds entity for example),
1.linking the MotorsAds entity to its MotorsFavorite entity through that code:
/**
* #ORM\OneToMany(targetEntity="Minn\AdsBundle\Entity\MotorsFavorite",
* mappedBy="motors",cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=true)
*/
private $favorites;
2.Defining the MotorsFavorite entity as fellows:
<?php
namespace Minn\AdsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* MotorsFavorite
*
* #ORM\Table(
* uniqueConstraints={#ORM\UniqueConstraint(name="unique_fav_motors",
* columns={"user_id", "motors_id"})})
* #ORM\Entity(repositoryClass="Minn\AdsBundle\Entity\MotorsFavoriteRepository")
* #ORM\HasLifecycleCallbacks()
*/
class MotorsFavorite {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Minn\UserBundle\Entity\User")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\ManyToOne(targetEntity="Minn\AdsBundle\Entity\MotorsAds", inversedBy="favorites")
* #ORM\JoinColumn(nullable=false, onDelete="CASCADE")
*/
private $motors;
//...
}
As you can see, the linkage between the MotorAds and MotorFavorite is a hard linkage, which means that I have to create a Favorite entity for each Ads entity I create (FavoriteMotors, FavoriteRealestate, FavoriteElectronics, ...). This is a long and repetitive work.
So my question is:
1.Creating a super mapped class called SuperMappedFavorite which will only include the $id and $user attributes will reduce the repetitive work. But what about the the attribute $motors? $motors is hardly linked to the entity MotorsAds as you see here:#ORM\ManyToOne(targetEntity="Minn\AdsBundle\Entity\MotorsAds", inversedBy="favorites"). All the burden of the work is in the setters and getters of $motors.
2.Is it possible to make the target entity an interface like this:
<?php
// SuperMappedFavorite.php
// ...
#ORM\ManyToOne(targetEntity="Minn\AdsBundle\Favorite\FavoriteAwareInterface", inversedBy="favorites")
private $object;
// ...
and the MotorsAds entity will be implementing in this the FavoriteAwareInterface
If anyone has a good link/article regarding this kind of issue, I will be happy to have it.
Thanks.
Yes, you can set an interface as target entity, as described in the Symfony documentation.
The process is basically:
defining the interface (your Minn\AdsBundle\Favorite\FavoriteAwareInterface),
setting the interface in the parent entity (as you already did),
implementing the interface in a different entity (would be class MotorsFavorite implements FavoriteAwareInterface) – and yes, it can also be derived from a mapped superclass,
and then telling Doctrine to use your implementation through the doctrine.orm.resolve_target_entities config parameter.
See the documentation for details and a code example.
What is the best way to handle a File entity where you have multiple ManyToOne relationships.
Let's say I have 5 entities that has a OneToMany relationship with the File entity.
File.php
/**
* #ORM\ManyToOne(targetEntity="Entity1", inversedBy="files")
* #ORM\JoinColumn(name="entity1_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
*/
private $entity1;
/**
* #ORM\ManyToOne(targetEntity="Entity2", inversedBy="files")
* #ORM\JoinColumn(name="entity2_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
*/
private $entity2;
and so one....
Entity1.php
/**
* #ORM\OneToMany(targetEntity="File", mappedBy="entity1" , cascade={"persist", "remove"}, orphanRemoval=true)
*/
protected $images;
The great thing about the above is the getter and setter are set, I can persist and save to the database automatically. The relationship is set and I can load the files by just calling $entity1->getFiles().
What I don't like is every time I want to add another entity that has a OneToMany with File it creates a new column in the database so potential I could have 10 columns referencing Ids from other entities.
What I would like to achieve is saving the class of the entity in the class field and saving the id of the record in an id field but also somehow still allowing the persist and collection saving to work.
entity_id | class
------------------------------------------
2 | ProjectBundle/Entity/Entity1
3 | ProjectBundle/Entity/Entity2
You don't need the class field at all.
Use Doctrine's inheritance mapping by creating a base class for all entities you want to refer from File:
/**
* #ORM\Entity()
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="entityType", type="string")
* #ORM\DiscriminatorMap({
* "entity1" = "Entity1",
* "entity2" = "Entity2"
* })
*/
abstract class BaseEntity
{
/**
* #ORM\ManyToMany(targetEntity="File", mappedBy="entities" , cascade={"persist", "remove"}, orphanRemoval=true)
*/
protected $images;
}
/**
* #ORM\Entity
*/
class Entity1 extends BaseEntity
{
...
}
/**
* #ORM\Entity
*/
class Entity2 extends BaseEntity
{
...
}
This way you can refer to both Entity1 and Entity2 by their base class from File. When calling getEntities, Doctrine creates instances of the proper class "automatically", based on the discriminator value of each entity.
File
/**
* #ORM\ManyToMany(targetEntity="Entity", inversedBy="images")
* #ORM\JoinColumn(name="entity_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
*/
protected $entities;
OneToMany, ManyToOne become ManyToMany because now the file may have many entities.
I have several bundles in my app and I would like to have relations between tables.
One is my User(StoreOwner) which is in UserBundle, and the second is Store in StoreBundle.
The relation between them is OneToMany (User -> is owner of -> Store).
Store
/**
* Description of Store
*
* #ORM\Table(name="Store")
* #ORM\Entity(repositoryClass="Traffic\StoreBundle\Repository\StoreRepository")
* #author bart
*/
class Store extends StoreModel {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string $name
*
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank(
* message="Please provide your shop name"
* )
*/
protected $name;
/**
* #ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\StoreOwner", inversedBy="stores")
*
*/
protected $owner;
}
StoreOwner
/**
* #ORM\Entity
*
*/
class StoreOwner extends User implements StoreOwnerInterface {
/**
* #var type ArrayCollection()
*
* #ORM\OneToMany(targetEntity="Traffic\StoreBundle\Entity\Store", mappedBy="owner", cascade={"persist"})
*/
protected $stores;
}
My question is:
Is there any solution to avoid dependency between StoreBundle and UserBundle and keep relations between Entities in Doctrine?
This is a valid concern in my opinion. Two-way dependencies between bundles are a smell.
One way of solving the dependency issue is moving your entities out of the bundles into a more general namespace. This way both bundles will depend on the same "library" but won't depend on each other directly.
I recently wrote a blog post on how to do it: How to store Doctrine entities outside of a Symfony bundle?