Symfony2, Doctrine, Empty associative entity - symfony

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

Related

How to fix broken doctrine relations?

There are two entities:
"Listings" that has the following ManyToOne relation:
/**
* #ORM\ManyToOne(targetEntity="Catalog", inversedBy="Listings",)
* #ORM\JoinColumn(name="catalog_id", referencedColumnName="catalog_id")
*/
protected $catalog;
This table already has some data
Now, after I imported some data manually to the Catalog table I would like to fix all the relations so the Listings table points to the right catalogs.
I wonder if there is any easy way to do it without iterating through all catalogs and listings.
I think the best way to fix your datas is to use a command that will fix them. You create a method in your repository with a limit if you have lot of entity to get corrupted datas then update. doc

Doctrine2 OneToMany with multiple Entities

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 { ... }

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.

Symfony2 and Doctrine - ManyToOne

I am trying to understand Symfony2, but there is something that is not making sense to me. I reversed engineered an existing database to produce my entities, so maybe thats the problem.
I have a table called availability_alert, nothing special, a few fields including an id (which is the primary key). This table has no link to anything else.
I then have a second table called booking_class, once again nothing special, but it does have the field $availabilityAlert which links to the availability_alerts tables id.
In essence, an Availability Alert can have one or many Booking Class.
Now in my booking class entity, I have the link
/**
* #var \AlertBundle\Entity\AvailabilityAlert
*
* #ORM\ManyToOne(targetEntity="AlertBundle\Entity\AvailabilityAlert")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="availability_alert_id", referencedColumnName="id")
* })
*/
private $availabilityAlert;
So this all looks ok. The setter for it though came out like so
public function setAvailabilityAlert(\AlertBundle\Entity\AvailabilityAlert $availabilityAlert = null)
{
$this->availabilityAlert = $availabilityAlert;
return $this;
}
So that appears to take an AvailabilityAlert Object as a parameter, not the AvailabilityAlert id?
So with the above, I am presuming doing something like this in my controller will not work?
$alert = new AvailabilityAlert();
$bookingClass = new BookingClass();
$bookingClass->setAvailabilityAlert($alert->getId());
Could someone give me some advice on whether things are correct here, or if I should be doing something else? Essentially AvailabilityAlert should be a static table, other tables link to this.
Any advice appreciated.
Thanks
Yes this is correct.
In the Doctrine world, you are working with objects, not with integers or strings, when it comes to relationships between Entities.
You can read more about Doctrine 2 relationships here: http://symfony.com/doc/current/book/doctrine.html#entity-relationships-associations
That's correct. You don't use integers, strings. It's because the relationships are given in entity annotations and Doctrine uses them to figure out what is used exactly to reference the one object from the other. This even let you change how objects reference themselves - for example you change the id to a compound primary key in AvailabilityAlert class and your code wouldn't change much except for the annotations.

Doctrine generate code with other naming conventions

We are starting with Symfony2 and Doctrine. I need to select some data from tables that already exist. These tables and column names do not use the naming conventions as defined by Doctrine.
I was wondering if I could create my own naming scheme somewhere. Mainly, we use PascalCase table and columnnames, without underscored. This results in Entity properties like $firstpromotiondatetime while the column is FirstPromotionDateTime, so i'd like my property to be firstPromotionDateTime.
You don't necessarily need to use doctrine's entity generator to generate new entities, you can also manually do it. However, you can also generate the entities via the command line, and then open your entity classes and change the property names as you see fit. Just make sure that the doctrine mapping (use the following annotation for instance) is still pointing to the correct column name in your database.
/**
* #var \DateTime $firstPromotionDateTime
*
* #ORM\Column(name="FirstPromotionDateTime", type="datetime")
*/
private $firstPromotionDateTime;
Edit:
If you manually change the property names, also make sure you have correctly modified the setters & getters as well.

Resources