How can I select all relations automatically in Doctrine? - php

I have a rather big number of classes related to each other via various relations (OneToOne, OneToMany, ManyToOne as well as Class- and Joined Inheritance which in turn again have multiple relations.
When I want to select all of a parent class via a simple ->findAll(), I get around 10 extra queries per Entity. I can reduce this number by a limited extend by unsing the a QueryBuilder with ->leftJoin() and ->addSelect(). Some relations do not join because reasons. Still, it gets a pretty nasty long list of addSelect()s. Is there a way to join and select all relations automatically?

Try using "JoinTable" or "JoinColumn"
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#jointable

The automatical join can be achieved by setting the fetch-mode of the associations to eager (e.g. on ManyToOne:
/**
* #ManyToOne(targetEntity="Cart", cascade={"all"}, fetch="EAGER")
*/
, see the docs)
To set the fetch mode programatically, refer to another StackOverflow question.

Related

Doctrine 2.5 Unexpected association fetch behavior [Symfony 3]

I have 3 entities associated this way:
Don't worry, I've set associations using annotations, but I thought the following mix would be lighter/cleaner to expose my issue
Post
#ORM\ManyToOne(targetEntity="User", fetch="EAGER")
- author
User
#ORM\OneToOne(targetEntity="Vip", mappedBy="user", fetch="EAGER")
- vip
Vip
# Notice that the primary key of vip is a foreign key on User primary
#ORM\id
#ORM\OneToOne(targetEntity="User", inversedBy="peliqan", fetch="EAGER")
#ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
- user
As you can see, all is set to be eagerly fetched.
What do I need?
I would like to retrieve Posts sets along with both Users & Vip infos ONLY, using a single query. (see edit)
Right now, for every post entry I get one extra query:
SELECT t0.valid AS valid_1, ...
FROM vip t0
INNER JOIN user t10 ON t0.user_id = t10.id WHERE t0.user_id = ?
when:
I execute this
$results = $qb
->select('ps')
->leftJoin('ps.author','u')->addSelect('u')
->add('where', $qb->expr()->in('ps.id', $ids))
->getQuery()->getResult();
and even while I try to enforce FETCH_EAGER mode like this
->getQuery()
->setFetchMode('AppBundle\Entity\User', 'vip', ClassMetadata::FETCH_EAGER)
->getResult();
Note:
I managed to get rid of extra query by enforcingQuery::HYDRATE_ARRAY upon getResult() call.
Queries vanished which saved a third of the initial time.
The downside here is that while retrieving association as arrays, I could not take advantage of Symfony\Component\Serializer\Annotation\Groups any more to filter entities properties and had to manually edit result set in order to remove/transform some values.
EDIT
Wilt answer is okay for the original post. I did not expose my issue the right way. I told I want to retrieve Vip infos because I thought it was a good way to get rid of the extra query I talk above. Actually I do not need Vip infos but omitting ->leftJoin('u.vip','v')->addSelect('v') makes doctrine issue the extra query which gather Vip infos! Is there a way to prevent doctrine from executing this query?
There are two kinds of join queries in Doctrine2:
1) Regular joins
2) Fetch joins
Check the documentation chapter 14.2.2. Joins for more details.
So if you want to fetch join vips you should addSelect and leftJoin them inside your query as follows:
$results = $qb
->select('ps')
->addSelect('u')->leftJoin('ps.author','u')
->addSelect('v')->leftJoin('u.vip','v')
->add('where', $qb->expr()->in('ps.id', $ids))
->getQuery()->getResult();
UPDATE
Update after your comment:
I thought including vip in the result set would be the best way to get rid of the extra query
You cannot get rid of the extra query because you cannot lazy load the inverse side of a one-to-one relationship. Refer also to this post for more details:
This is expected behavior. Inverse sides of one-to-one associations can not be lazy, technically. There is no foreign key on the inverse side, hence it is impossible to decide whether to proxy it or not. We must query for the associated object or join it. Note that this only affects inverse sides of single-valued associations, that is, really only the inverse side of bidirectional one-to-one associations.
A solution could be to inverse the relationship so user becomes the owning side of the relationship. I that case you can at least lazy-load Vip inside your User entity. The lazy load problem would move to the Vip side, meaning you could not lazy-load your User in Vip any longer.
Otherwise you could make your query return a Partial object to prevent loading of Vip, but in general you should be very careful with this approach.

One entity to multiple tables with symfony/doctrine

Introduction
I'm working on a symfony3 project and I have a lot of different "types" for certain entities. What I mean by this is that, I have a TypeUser, TypeEvent, TypeMeeting tables.
These tables will only have an id and a label.
Problem:
When using doctrine, I can link 1 entity to 1 table using the anotations, like so:
/**
* #ORM\Entity
* #ORM\Table(name="TypeUser")
*/
However, I would like this part to be completely generic. How can I specify the Table name depending on the type I need?
Is there another alternative when using Doctrine other than the annotations to make this possible?
I would really like to avoid making n entities for n tables, when they are very similar in name and in structure.
Question:
Is it possible to make one generic entity to match a specific TypeXXXX table, to reduce redundancy? If so how would I go about doing it?
Similar Doctrine 2.1 - Map entity to multiple tables
Symfony book on doctrine

best way inner join symfony2

I have a lot of times 2 or more entities who are related to.
For example:
entity avance(): with attributes: id, userId, questionnaireId, questionId
entity questionnaire: id, name, questionsNbres
entity questions: id, question, responseA, responseB, responseC, correctResponse
When I call an avance entity, it is complicated to find the questionnaire name attribut, I have to do a lot of foreach to find the correct one. The same for all the questions that are related to.
I'm sure that a best solution exist, for example with a inner join, but I don't know how to do this in symfony.
You get two tools that responds to theses problems.
You can setup annotations in yours models to automate data fetching. This is the JoinColumn annotation
However, because the JoinColumn usually over-fetching, my recommandation would be to put in place a custom repository, it allow to create complex query with joins.
In your case, I think you need a left join aka "extends my datas, with another data set".

Doctrine Inheritance and MySQL Join Table Limit

I have a problem with 61 join table limit of mysql. I have 57+ different classes extending Base Class which contain association to comments, likes, tags. And MySQL is crashing when i get most commented. Doctrine has to join whole discriminator maps and comments itself and order by COUNT(comments).
Is there way to fix that ?
And is there another way to achieve comments for different types of entities without inheritance and copying same association all over again?
Here is sample schema of Entities. When I want to add new Entity Type with comments,likes I just extends BaseClass to receive these features.
If I understand correctly what you're trying to do is have many different entity types which can be commented on.
First thing to do would be to step back from Doctrine and thing about the simplest table structure you would need to accomplish this.
Something like this may suffice:
Comments
_______________________
| id | type | entity_id |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
It is nice in Doctrine to have bi-directional relationships in your base class for convenience but sometimes they are not the best choice. Maybe it would simplify your architecture to perform a query directly on the comments table by entity type and id.
You may also want to consider removing the Base class and having each entity be standalone.
Since a blog post can exist in a context where it does not have comments (on a blog that doesn't allow commenting for example) then $blog->getComments() wouldn't make much sense.
Making this change you could do something like this instead:
$comments = $commentsRepository->findCommentsForEntity($entity);
$commentsCount = count($comments);
and the repository could generate the needed query passing the entity as the entity_id parameter and setting the required comment type based on the entity type.

Doctrine many-to-one association keeps pulling all rows from associated table

I'm attempting to set up a many-to-one relationship between a series of sales memos and transaction records.
/**
* #var TransactionInterface
*
* #ORM\ManyToOne(targetEntity="Twb\Common\Model\Broker\TransactionInterface")
* #ORM\JoinColumn(name="FormNoSeller", referencedColumnName="Form")
*/
private $formnoseller;
/**
* #var TransactionInterface
*
* #ORM\ManyToOne(targetEntity="Twb\Common\Model\Broker\TransactionInterface")
* #ORM\JoinColumn(name="FormNoBuyer", referencedColumnName="Form")
*/
private $formnobuyer;
They are split between two different bundles at the moment ('SalesBundle' and 'BrokerBundle'), and with that in mind I am using interfaces from the SalesMemo entity in SalesBundle to the Transaction entity in BrokerBundle.
For some reason, when I reference either or both of $formnoseller and $formnobuyer in my forms, I notice in dev.log that, after selecting all Transaction rows matching the $formnoseller and/or $formnobuyer fields in the SalesMemos, Doctrine tries to SELECT all rows in the Transaction table (the entity for which TransactionInterface references). This is a bit of a problem, since there is an innumerable amount of rows in the DB, which takes up a lot of memory.
Is there any way to have Doctrine avoid selecting all rows with associations? Or am I even understanding properly how Doctrine does associations? Many thanks for any help.
My understanding of your problem is that you're using an Entity Field Type for $formnoseller and $formnobuyer (or you don't specify the type). Giving the choice to select any élément from the underlying table is the expected behaviour for the Entity Field Type (Used by default for OneToMany relationships)
If you don't whant a select list of all the elements of your table for those Fields, you should use an other form field type. You should also have a look at data transformers in the documentation.
If it were me, I would write a stored procedure and do an inner or outer join as appropriate.
Once upon a time, they called this "client server" code. About 15 years ago, it created such a mess the whole industry moved to n-tier development. I'd like to know how the table joins got placed back into the presentation tier again? ORMs and LINQ-to-SQL are a return to client/server".
If you have to do it this way, do the join in LINQ on the Models. Do not do it with the ORM language.

Categories