Doctrine2 OneToMany with multiple Entities - symfony

I have three kinds of Users, every user has some FiscalData and is linked to a UserCredential entry.
Which translates in:
UserX (X=1,2,3) has two FKs referring to FiscalData and
UserCredential tables.
Using Doctrine2, reading the docs http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/association-mapping.html, I believe to need the MappedSuperClass pattern.
I have also read the following questions:
Doctrine 2 - One-To-Many with multiple Entities
Many-To-One with multiple target entities
Doctrine2, Symfony2 - oneToOne with multiple entities?
But in the docs is clearly stated that
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.
So, how to achieve what I'm trying to achieve, which is a bidirectional relationship between UserX and FiscalData/UserCredential? (so that f.e. via Doctrine I can get a UserCredential and check what kind of profiles it has associated)
Any complete minimal code example showing how to enforce the relationship I'm looking for (and not just the MappedSuperClass inheritance shown in the docs will be highly appreciated.

Use an abstract entity instead of MappedSuperClass. Single-table is usually the way to go unless you're sure you want class/table.
<?php
/**
* #ORM\Entity
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({
* "mentor" = "Mentor",
* "protege" = "Protege"
* })
*/
abstract class User { ... }

Related

Doctrine inheritance performance

In my app, I have a few entities that share some common properties. Example entities:
Image
Video
Event
ForumPost
I want these classes to share some common functions, such as:
Have a list of comments, as well as keep counts of comments
Voting
List of subscribers and subscriber counts
View counts
etc..
I really would not want duplicate these functions for each of the entities above. So... I've been reading about Doctrine inheritance mapping and I've come up with an idea that "Class Table Inheritance" is the most elegant way to solve this. The plan was as follows:
Create a parent Post entity
/**
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="type", type="string")
*/
class Post
{
protected $likeCount;
protected $subscriberCount;
protected $subscribers;
// ...
}
Have many-to-one relations of likes, comments, votes, etc to Post entity:
class PostLike
{
/**
* #ORM\ManyToOne(targetEntity="Post")
* #ORM\JoinColumn(name="post_id", referencedColumnName="id", nullable=false)
*/
protected $post;
}
Have the Image, Video, etc entity extend Post:
class Video extends Post
{
// Post properties become available here
}
All worked like charm until I looked at the actual queries Doctrine is executing. The problem is: whenever I select a PostLike, doctrine will eager-load Post entity as well, which will in turn join all the child tables as well. So for example, a simple PostLike remove operation triggers a join on 4 tables and will actually join even more tables as my hierarchy grows.
If I have 10 entities extending Post, that would make a join on 10 tables. I am not a fan of premature optimization, but this just doesn't sound like a good plan.
In Doctrine documentation, this behavior is actually mentioned at "6.3.2. Performance impact".
Finally, my question: What are my other options to create reusable tables with doctrine?
I was thinking to do a One-To-One mapping (Post entity being a property of the Video, as well as the Image, etc), but I've read in a few blogs that One-to-One should be avoided with doctrine also for performance reasons.
thanks!
Edit and ansewer to Raphaƫl MaliƩ regarding Mapped Superclasses:
I am aware that I can use Mapped Superclass and Trait features to avoid code duplication. However, these features allow me to reuse the code but don't allow me to reuse sql tables. Here is an example:
Say I want Image, Video and Event entities to have comments. I can create one Mapped Superclass AbstractComment and then extend it in ImageComment, VideoComment, EventComment. Next, I want to add comment votes. Since I can't reuse same sql table, I will need to create ImageCommentVote, VideoCommentVote, EventCommentVote, etc. Then I want users to be able to report abusive comments. There goes again: ImageCommentReport, VideoCommentReport, EventCommentReport. You get the idea. Every feature that I want to add to comment will need a separate table for each entity like image, video, etc. Lots of tables.
The reasons why I prefer to use centralized approach instead of using traits with many tables:
Easier administration. I can easily search/edit/delete comments of the entire app by sending a query to one table.
The same controller can handle actions for multiple entities, no need to create one abstract and many concrete controllers
Showing user notifications about new comments/likes etc is MUCH easier when actions are centralized.
I am also aware of the drawbacks of such system, but I can live with them for now. My question is how to get Doctrine to do what I want without actually joining everything when not necessary.
In my opinion you are doing wrong. Your entities have nothing in common, except some functionalities, so there is no reason to use single table or class table inheritance.
You should use Traits: http://php.net/manual/en/language.oop5.traits.php
It works with Doctrine. You define one or several traits with some properties / methods with annotations, and then you import these traits in your entities.
You can also use a Mapped Superclass : http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html#mapped-superclasses which is quite close to your example, but with a different entity annotation.
After some thought, your decision really depends on the size of your database. If it is big the performance hit of the Class Tabel approach might be unacceptable. But if your tables are small, the performance hit is minor and you can use all the advantages of Class Table Inheritance, which you discussed yourself in your post.

FOSUserBundle One-To-One mapped Entity not saved

Hi Folks i have a question regarding implementing One-To-One in Entity FosUserBundle.
User Entity Has One To One Mapping With Profile Entity. I have override the basic RegistrationFormType as per shown in the FOSUserBundle's documentation. record is also saved in both table. but mapping entities show me blank data. Please find respected gist file for same.
UserEntity Gist- https://gist.github.com/ajaypatelbardoli/2f0c81cbdf3b0d136785
ProfileEntity Gist - https://gist.github.com/ajaypatelbardoli/fd02025fd338ed90545e
ProfileFormType gist - https://gist.github.com/ajaypatelbardoli/18ef99a3d0bd1198debc
RegistratonFormType Gist - https://gist.github.com/ajaypatelbardoli/09c047425032391c2445
The problem with your implementation is that you do not update the owning side of the bidirectional association. The Doctrine documentation explicitly states:
See how the foreign key is defined on the owning side of the relation, the table Cart.
In your case the owning side is Profile which you can update automatically in setUserId() as folows:
public function setUserId(\XXXX\Bundle\UserBundle\Entity\User $userId = null)
{
$this->userId = $userId;
$userId->setProfile($this);
return $this;
}
You can access the data from both sides of the relation without problems, Doctrine will look up the corresponding entries.

Doctrine2 / Symfony2 - Multiple entities on same table

In a Symfony2 application I have a MainBundle and distinct bundles which can be enabled or not. In the MainBundle I need to have the Model and a basic Entity. In an OtherBundle an Entity with the same table name than Entity in MainBundle.
Fixtures in MainBundle need to be loaded with or without the other bundles than MainBundle :
MainBundle
- Model
- Entity (Table name "test")
- Fixtures
OtherBundle
- Entity (Table name "test")
- Fixtures
OtherBundle2
- Entity (Table name="test")
- Fixtures
If i used the #ORM\MappedSuperclass for the Model, a #ORM\Entity for the Entity in MainBundle and #ORM\Entity in OtherBundle then Doctrine2 stop with the error "table already exists".
I cant use the Inheritance table as my model dont need to know about other entities in the other bundles. The #ORM\DiscriminatorMap cant point to OtherBundle.
Is there a way to do this ?
As mentioned by Jasper N. Brouwer it's esentially the same entity and the same table, so there is no point in doing what you're trying to do.
Create your entity in a bundle named for example "SharedEntityBundle" and use resolve_target_entity to relate to this entity from other bundles without them knowing about eachother.
http://symfony.com/doc/current/cookbook/doctrine/resolve_target_entity.html
That being said, there seems to be a solution with multiple entity managers:
Symfony 2 / Doctrine 2: Two Entities for the same table, use one in favour of the other

Symfony2, Doctrine, Empty associative entity

Let' imagine we have several tables: table_item, table_category, table_items_status.
Which is updated by service in single mode (no relations) using their own entity.
Can i, and how, create one entity that will have only relatioship of this tables, for example something like that....
**
* #ORM\OneToOne(targetEntity="table_item")
* #ORM\JoinColumn(name="itemID", referencedColumnName="itemID")
**
private $tableItemIDByItemID
// ... getter\setter
**
* #ORM\Column(type="integer")
**
private $itemID;
// ... getter\setter
In php code i want simply call
$entity->setItemID(123);
$result = $entity->getTableItemIDByItemID();
And will get ArrayCollection() from table_item by itemID.
Main thing that I want create extra entity only with relationships for several tables and only unidirectional. I need this for creating entity without touching another for relationships.
You should look into entity repositories. These provide some basic functionality queries but you can extend them with your own custom queries (eg: getItemIDByItemID). It's called the repository pattern. An entity is just a object representation of your database.
Symfony2 manual:
"It's a good idea to create a custom repository class for your entity and add methods with your query logic there."
Some more information about repositories:
http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes
http://www.zalas.eu/doctrine2-and-symfony2

Symfony2 - how to construct id = product mapping

I want to do mapping that does this:
User can own multiple Games. Games can have multiple owners.
I have id column in game table and game and user columns in ownership table. How I can connect these fields? I want to have game and user fields in ownership related to user and game tables.
I tried OneToMany and ManyToMany, but the first one results in generating additional columns. I don't want to insert anything in game table.
--edit--
My #ManyToMany code:
/**
* #ORM\ManyToMany(targetEntity="Ownership")
* #ORM\JoinTable(name="ownership",
* joinColumns={JoinColumn(name="user", referencedColumnName="id")},
* inverseJoinColumns={JoinColumn(name="game", referencedColumnName="id")}
* )
*/
It causes an error in Symfony's command line:
[Doctrine\Common\Annotations\AnnotationException]
[Semantical Error] Couldn't find constant JoinColumn, property GameShelf\Us
ersBundle\Entity\User::$ownership.
Of course you need many to many relations if, like you said:
User can own multiple Games. Games can have multiple owners.
The Doctrine should create third table (which could contains only two foreign keys: game_id and ownership_id).
The error is caused because Doctrine dosen't know what JoinColumn is. You just did wrong annotation because you forgot (again! ;)) precede JoinColumn with `#ORM. The right annotation should look like this:
/**
* #ORM\ManyToMany(targetEntity="Ownership")
* #ORM\JoinTable(name="ownership",
* joinColumns={#ORM\JoinColumn(name="user", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="game", referencedColumnName="id")}
* )
*/
Sounds like you need to introduce a new entity (something like OwnedGame) that holds the connection between a Game and an Owner (and possibly its own properties like when it was bought and such).
Then OwnedGame would be ManyToOne with Game and with Owner, and neither Game nor Owner would need to include a new column.
Your description of what you want doesn't fit at all.
On the one hand you want to have a relation between only Users and Games, and only between Games and Owners.
But on the other hand you say that Users and Owners are related in some way, but Games and Users aren't.
You should read through
http://docs.doctrine-project.org/en/2.0.x/reference/association-mapping.html#many-to-many-unidirectional
and try to find what you really need.
I would recommend a ManyToMany relation with a join table. Thus you don't have to deal with additional fields in your tables

Resources