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 :)
Related
I would like to have a "post" with an identifier. This one could be classified in another "post" by storing the identifier of his parent.
I tried to do like this:
class Post {
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #ORM\OneToMany(targetEntity="Post", mappedBy="Id_Post_Parent")
*/
private $Id_Post;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Post", inversedBy="Id_Post")
* #ORM\JoinColumn(name="Id_Post", referencedColumnName="Id_Post", nullable=true)
*/
private $Id_Post_Parent;
...
}
but I have this error when i'm checking with doctrine:schema:validate :
[FAIL] The entity-class App\Entity\Post mapping is invalid:
The association App\Entity\Post#Id_Post_Parent refers to the inverse side field App\Entity\Post#Id_Post which is not defined as association.
The association App\Entity\Post#Id_Post_Parent refers to the inverse side field App\Entity\Post#Id_Post which does not exist.
The referenced column name 'Id_Post' has to be a primary key column on the target entity class 'App\Entity\Post'.
Can someone help me to fix this ?
There is small logical error with your structure - your ID_Post variable tries to be both the primary key (the ID) and the collection association side. I didn't check this syntax in too much details (you can find an example of this association along with most of the other associations from doctrine documentation: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/association-mapping.html#one-to-many-self-referencing), but basically you need to add the children association separately to your entity like this:
class Post
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Post", inversedBy="postChildren")
* #ORM\JoinColumn(name="id_parent_post", referencedColumnName="id", nullable=true)
*/
private $postParent;
/**
* #ORM\OneToMany(targetEntity="Post", mappedBy="postParent")
*/
private $postChildren;
public function __construct() {
$this->postChildren = new \Doctrine\Common\Collections\ArrayCollection();
}
}
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 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