doctrine / orm

Doctrine Object Relational Mapper (ORM)
https://www.doctrine-project.org/projects/orm.html
MIT License
9.93k stars 2.51k forks source link

DDC-2131: Fetch join not working on class table inheritance #2819

Closed doctrinebot closed 8 years ago

doctrinebot commented 11 years ago

Jira issue originally created by user alsar:

I have an entity Appointment, that is associated with application forms. I have an abstract class AbstractApplicationForm that has multiple (6) child classes.

The mapping info for the abstract application form looks like this:

/****
 * @ORM\Entity()
 * @ORM\Table("applicationform")
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 * @ORM\DiscriminatorMap({
 *     "slc" = "SlcApplicationForm",
 *     "vsdb" = "VsdbApplicationForm",
 *     "vss" = "VssApplicationForm",
 *     "opzd" = "OpzdApplicationForm",
 *     "vzu" = "VzuApplicationForm",
 *     "opzdvzu" = "OpzdVzuApplicationForm"
 * })
 */
abstract class AbstractApplicationForm
...

I have an OneToMany connection from Appointment to AbstractApplicationForm. When i construct an DQL, to get an appointment with only specific application forms it gets wrong translated into SQL.

For instance if the query builder looks like this:

$qb->select('ap, af')
   ->from(Appointment::getClass(), 'ap')
   ->leftJoin('ap.applicationForms', 'af', 'WITH', 'af.id = 123456789')
   ->andWhere('ap.id = :appointment')
   ->setParameter('appointment', $appointment);

So with this dql i should get an appointment with filtered application forms. In this case it whould return an appointment with only one application form (that with the id 123456789). But it returns all associated application form instead on only that with the id 123456789, because it ignores the with clause.

Here is the SQL that gets generated from the DQL:

SELECT ...
FROM program*execution_activity_appointment p8_, program_execution_activity_appointment p0*
LEFT JOIN applicationform a1* ON p0_.id = a1_.appointment*id 
LEFT JOIN applicationform*slc a2_ ON a1_.id = a2*.id 
LEFT JOIN applicationform*vsdb a3_ ON a1_.id = a3*.id 
LEFT JOIN applicationform*vss a4_ ON a1_.id = a4*.id 
LEFT JOIN applicationform*opzd a5_ ON a1_.id = a5*.id 
LEFT JOIN applicationform*vzu a6_ ON a1_.id = a6*.id 
LEFT JOIN applicationform*opzdvzu a7_ ON a1_.id = a7_.id AND (a1*.id = 123456789) 
WHERE p0_.id = 1

The problem i see here is that the AND is added to the last join (applicationform_opzdvzu). But it should added to the first join (applicationform). Because the first join represents the parent entity (AbstractApplicationForm). There is also a problem in the FROM clause. The problem is that program_execution_activityappointment p8 is never used anywhere else, except in the from clause.

The generated query should look like this:

SELECT ...
FROM program*execution_activity_appointment p0*
LEFT JOIN applicationform a1* ON p0_.id = a1_.appointment_id AND (a1*.id = 123456789) 
LEFT JOIN applicationform*slc a2_ ON a1_.id = a2*.id
LEFT JOIN applicationform*vsdb a3_ ON a1_.id = a3*.id
LEFT JOIN applicationform*vss a4_ ON a1_.id = a4*.id
LEFT JOIN applicationform*opzd a5_ ON a1_.id = a5*.id
LEFT JOIN applicationform*vzu a6_ ON a1_.id = a6*.id
LEFT JOIN applicationform*opzdvzu a7_ ON a1_.id = a7*.id
WHERE p0_.id = 1

This sql works as expected.

I hope i was clear enough what the problem is.

doctrinebot commented 11 years ago
doctrinebot commented 11 years ago

Comment created by @beberlei:

This is a duplicate of DDC-1256

doctrinebot commented 11 years ago

Comment created by @beberlei:

This is not easy to fix as you can see DDC-1256 is open for a while. However in your case there is an easy solution, why not fetch the appointment form by id, and then go the association to the appointment? This is the inverse operation on that relation, but suits your needs perfectly.

doctrinebot commented 11 years ago

Comment created by gseric:

Benjamin, this issue is duplicate of duplicate (according to your status changes). So original issues is DDC-349, but this error was introduced in 2.3. DDC-349 was created way back in 2010 so it can't be the same problem.

As far as I can tell, the problem was introduced with "Arbitrary join support" feature: https://github.com/doctrine/doctrine2/pull/368 (SqlWalker.php changes) Basically, in version 2.3 and later WITH statement is wrongly handled after SqlWalker::_generateClassTableInheritanceJoins() Before 2.3 it was handled before.

You can see it clearly in this issue's description: should be (< 2.3): LEFT JOIN ON AND but we get (>= 2.3): LEFT JOIN ON AND

This issue can't be easily avoided in situation where you want to SELECT only one child row per parent row (based on some condition). WITH is natural and fastest option.

doctrinebot commented 11 years ago

Comment created by gseric:

This bug seems to be recognized and fixed (pull request ATM) in DDC-2506

doctrinebot commented 11 years ago

Comment created by @doctrinebot:

A related Github Pull-Request [GH-708] was closed: https://github.com/doctrine/doctrine2/pull/708

doctrinebot commented 10 years ago

Comment created by strate:

Why do you think that applying ON/WITH conditions only to base table is good? What if I will reference to fields from child entity in ON/WITH clause? I think that all user conditions should be applied to last join in class table inheritance joins, because only in that join references to all tables are available. Otherwise I get sql error.

doctrinebot commented 10 years ago

Comment created by strate:

https://github.com/doctrine/doctrine2/pull/886

doctrinebot commented 10 years ago

Comment created by @doctrinebot:

A related Github Pull-Request [GH-886] was closed: https://github.com/doctrine/doctrine2/pull/886

doctrinebot commented 9 years ago

Comment created by @doctrinebot:

A related Github Pull-Request [GH-708] was closed: https://github.com/doctrine/dbal/pull/708

doctrinebot commented 9 years ago

Comment created by @doctrinebot:

A related Github Pull-Request [GH-708] was assigned: https://github.com/doctrine/dbal/pull/708

doctrinebot commented 11 years ago

Issue was closed with resolution "Duplicate"