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.
Related
I want to make a many-to-many association between a mapped superclass CommonResource and an entity Author. We are using the syntaxis provided by the doctrine manual on this topic: association override doctrine. But when I validate the doctrine schema with php bin/console doctrine:schema:val I get the following error:
[Mapping] FAIL - The entity-class 'AppBundle\Entity\Author' mapping is invalid:
* The association AppBundle\Entity\Author#entities refers to the owning side field AppBundle\Entity\CommonResource#authors which does not exist.
I have a mapped superclass CommonResource:
/**
* Class CommonResource
* #ORM\MappedSuperclass()
* #ORM\HasLifecycleCallbacks
*/
class CommonResource
{
...
/**
* #var
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Author", inversedBy="entities")
*/
protected $authors;
}
And an entity Author:
/**
* Author
*
* #ORM\Table(name="author")
* #ORM\HasLifecycleCallbacks
*/
class Author
{
...
/**
* #var
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\CommonResource", mappedBy="authors")
*/
protected $entities;
}
And some entities with extend the mapped superclass, for example:
/**
* Itinerary
*
* #ORM\Table(name="itinerary")
* #ORM\Entity(repositoryClass="AppBundle\Repository\Itinerary\ItineraryRepository")
* #ORM\AssociationOverrides({#ORM\AssociationOverride(name="authors", joinTable=#ORM\JoinTable(
* name="common_resource_itinerary",
* joinColumns=#ORM\JoinColumn(name="common_resource_id"),
* inverseJoinColumns=#ORM\JoinColumn(name="itinerary_id")
* ))})
*/
class Itinerary extends CommonResource implements AllowedPermissionInterface, EnrollmentInterface, SluggableInterface, CommonResourceEntityInterface
{
...
}
Have I used correctly the doctrine syntaxis for association overrides with mapped superclasses or there is something wrong? I would appreciate any help :)
I am using Symfony 4 and with Doctrine where I have entities which have the same common attributes such as createdWhen, editedWhen, ...
What i would like to do is this:
Defining a kind of base entity that holds these common attributes and implements the setter and getter. And many entities which inherit from that base entity. The database fields should all be defined in the table of the respective sub entity (no super table or the like should be created in the db).
Example:
/**
* #ORM\Entity(repositoryClass="App\Repository\BaseRepository")
*/
class Base
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=127, nullable=true)
*/
private $createdWhen;
// Getter and setter
...
}
/**
* #ORM\Entity(repositoryClass="App\Repository\PersonRepository")
*/
class Person extends Base
{
/**
* #ORM\Column(type="string", length=127, nullable=true)
*/
private $name;
// Getter and setter
...
}
/**
* #ORM\Entity(repositoryClass="App\Repository\CarRepository")
*/
class Car extends Base
{
/**
* #ORM\Column(type="string", length=127, nullable=true)
*/
private $brand;
// Setter and getter
...
}
This should create the tables "person" and "car" (each with id, created_when) but no table base.
I would still like to be able to use the bin/console make:migration for updating the database schema.
Is this kind of approach possible with Symfony 4? If yes how would I define the entities and what do I have to change in terms of configuration, etc.?
You are looking for entity inheritance
Rewrite your code like so
/** #MappedSuperclass */
class Base
{
...
}
In fact, this is a part of Doctrine, here is what an official documentation says
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.
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 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