Details
Description
When using the Doctrine_Record::option('orderBy', ...) feature on a table definition, where that table is the target of a many-to-many join, the specified orderBy columns are applied to the relation table's alias. So for example, given the following definitions:
class User extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('uid', 'integer', null, array('primary' => true)); $this->option('orderBy', 'uid'); } public function setUp() { $this->hasMany('Group as groups', array('refClass' => 'UserGroup', 'local' => 'user_uid', 'foreign' => 'group_id')); } } class Group extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('gid', 'integer', null, array('primary' => true)); } public function setUp() { $this->hasMany('User as users', array('refClass' => 'UserGroup', 'local' => 'group_gid', 'foreign' => 'user_id')); } } class UserGroup extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('user_uid', 'integer', null, array('primary' => true)); $this->hasColumn('group_gid', 'integer', null, array('primary' => true)); } public function setUp() { $this->hasOne('User as user', array('local' => 'user_uid', 'foreign' => 'uid')); $this->hasOne('Group as group', array('local' => 'group_gid', 'foreign' => 'gid')); } }
the following queries:
$query = Doctrine_Query::create()
->select('u.*')
->from('User u')
->leftJoin('u.groups g WITH g.gid=?', 1);
echo $query->getSqlQuery() . "\n";
$query = Doctrine_Query::create()
->select('g.*')
->from('Group g')
->leftJoin('g.users u WITH u.uid=?', 1);
echo $query->getSqlQuery() . "\n";
will output the following:
SELECT u.uid AS u__uid FROM user u LEFT JOIN user_group u2 ON (u.uid = u2.user_uid) LEFT JOIN group g ON g.gid = u2.group_id AND (g.gid = ?) ORDER BY u.uid
SELECT g.gid AS g__gid FROM group g LEFT JOIN user_group u2 ON (g.gid = u2.group_gid) LEFT JOIN user u ON u.uid = u2.user_id AND (u.uid = ?) ORDER BY u.uid, u2.uid
The orderBy option() call is applied to the User definition. The SQL for the first query is correct (where User is on the left side of the join). The SQL for the second query (where User is on the right-most side of the join), however, is obviously incorrect (UserGroup doesn't even have a uid column). Basically, User's orderBy option is being applied to both the User table and its respective reference table, UserGroup, when it is the target of a join.
After digging through the source for a while, I believe I've come up with a patch for this issue (which should be checked by someone more knowledgeable of Doctrine's internals). Basically, in the Doctrine_Query::buildSqlQuery() function, a call is made to Doctrine_Relation::getOrderByStatement() with the reference table (UserGroup)'s alias (u2), which in turn makes a call to Doctrine_Table::getOrderByStatement() on the referenced table (User), filling in the ORDER BY clause with User columns using UserGroup's alias. My solution was to reorder the logic so that the test for a reference class is made before the initial call to getOrderByStatement() is made. It seems to work against my test case and the test cases in the repository. I'll post my patch momentarily.
This bug was first mentioned in the comments in DC-313, but the original ticket comes across as more of a feature request for the hasMany() orderBy feature.
Activity
| Field | Original Value | New Value |
|---|---|---|
| Description |
When using the {Doctrine_Record::option('orderBy', ...)} feature on a table definition, where that table is the target of a many-to-many join, the specified orderBy columns are applied to the relation table's alias. So for example, given the following definitions: {code} class User extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('uid', 'integer', null, array('primary' => true)); $this->option('orderBy', 'uid'); } public function setUp() { $this->hasMany('Group as groups', array('refClass' => 'UserGroup', 'local' => 'user_uid', 'foreign' => 'group_id')); } } class Group extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('gid', 'integer', null, array('primary' => true)); } public function setUp() { $this->hasMany('User as users', array('refClass' => 'UserGroup', 'local' => 'group_gid', 'foreign' => 'user_id')); } } class UserGroup extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('user_uid', 'integer', null, array('primary' => true)); $this->hasColumn('group_gid', 'integer', null, array('primary' => true)); } public function setUp() { $this->hasOne('User as user', array('local' => 'user_uid', 'foreign' => 'uid')); $this->hasOne('Group as group', array('local' => 'group_gid', 'foreign' => 'gid')); } } {code} the following queries: {code} $query = Doctrine_Query::create() ->select('u.*') ->from('User u') ->leftJoin('u.groups g WITH g.gid=?', 1); echo $query->getSqlQuery() . "\n"; $query = Doctrine_Query::create() ->select('g.*') ->from('Group g') ->leftJoin('g.users u WITH u.uid=?', 1); echo $query->getSqlQuery() . "\n"; {code} will output the following: {quote} SELECT u.uid AS u__uid FROM user u LEFT JOIN user_group u2 ON (u.uid = u2.user_uid) LEFT JOIN group g ON g.gid = u2.group_id AND (g.gid = ?) ORDER BY u.uid SELECT g.gid AS g__gid FROM group g LEFT JOIN user_group u2 ON (g.gid = u2.group_gid) LEFT JOIN user u ON u.uid = u2.user_id AND (u.uid = ?) ORDER BY u.uid, u2.uid {quote} The {orderBy} {option()} call is applied to the {User} definition. The SQL for the first query is correct (where {User} is on the left side of the join). The SQL for the second query (where {User} is on the right-most side of the join), however, is obviously incorrect ({UserGroup} doesn't even have a {uid} column). Basically, {User}'s {orderBy} option is being applied to both the {User} table and its respective reference table, {UserGroup}, when it is the target of a join. After digging through the source for a while, I believe I've come up with a patch for this issue (which should be checked by someone more knowledgeable of Doctrine's internals). Basically, in the {Doctrine_Query::buildSqlQuery()} function, a call is made to {Doctrine_Relation::getOrderByStatement()} with the reference table ({UserGroup})'s alias ({u2}), which in turn makes a call to {Doctrine_Table::getOrderByStatement()} on the referenced table ({User}), filling in the {ORDER BY} clause with {User} columns using {UserGroup}'s alias. My solution was to reorder the logic so that the test for a reference class is made before the initial call to {getOrderByStatement()} is made. It seems to work against my test case and the test cases in the repository. I'll post my patch momentarily. This bug was first mentioned in the comments in DC-313, but the original ticket comes across as more of a feature request for the {hasMany()} {orderBy} feature. |
When using the {{Doctrine_Record::option('orderBy', ...)}} feature on a table definition, where that table is the target of a many-to-many join, the specified orderBy columns are applied to the relation table's alias. So for example, given the following definitions: {code} class User extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('uid', 'integer', null, array('primary' => true)); $this->option('orderBy', 'uid'); } public function setUp() { $this->hasMany('Group as groups', array('refClass' => 'UserGroup', 'local' => 'user_uid', 'foreign' => 'group_id')); } } class Group extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('gid', 'integer', null, array('primary' => true)); } public function setUp() { $this->hasMany('User as users', array('refClass' => 'UserGroup', 'local' => 'group_gid', 'foreign' => 'user_id')); } } class UserGroup extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('user_uid', 'integer', null, array('primary' => true)); $this->hasColumn('group_gid', 'integer', null, array('primary' => true)); } public function setUp() { $this->hasOne('User as user', array('local' => 'user_uid', 'foreign' => 'uid')); $this->hasOne('Group as group', array('local' => 'group_gid', 'foreign' => 'gid')); } } {code} the following queries: {code} $query = Doctrine_Query::create() ->select('u.*') ->from('User u') ->leftJoin('u.groups g WITH g.gid=?', 1); echo $query->getSqlQuery() . "\n"; $query = Doctrine_Query::create() ->select('g.*') ->from('Group g') ->leftJoin('g.users u WITH u.uid=?', 1); echo $query->getSqlQuery() . "\n"; {code} will output the following: {quote} SELECT u.uid AS u__uid FROM user u LEFT JOIN user_group u2 ON (u.uid = u2.user_uid) LEFT JOIN group g ON g.gid = u2.group_id AND (g.gid = ?) ORDER BY u.uid SELECT g.gid AS g__gid FROM group g LEFT JOIN user_group u2 ON (g.gid = u2.group_gid) LEFT JOIN user u ON u.uid = u2.user_id AND (u.uid = ?) ORDER BY u.uid, u2.uid {quote} The {{orderBy}} {{option()}} call is applied to the {{User}} definition. The SQL for the first query is correct (where {{User}} is on the left side of the join). The SQL for the second query (where {{User}} is on the right-most side of the join), however, is obviously incorrect ({{UserGroup}} doesn't even have a {{uid}} column). Basically, {{User}}'s {{orderBy}} option is being applied to both the {{User}} table and its respective reference table, {{UserGroup}}, when it is the target of a join. After digging through the source for a while, I believe I've come up with a patch for this issue (which should be checked by someone more knowledgeable of Doctrine's internals). Basically, in the {{Doctrine_Query::buildSqlQuery()}} function, a call is made to {{Doctrine_Relation::getOrderByStatement()}} with the reference table ({{UserGroup}})'s alias ({{u2}}), which in turn makes a call to {{Doctrine_Table::getOrderByStatement()}} on the referenced table ({{User}}), filling in the {{ORDER BY}} clause with {{User}} columns using {{UserGroup}}'s alias. My solution was to reorder the logic so that the test for a reference class is made before the initial call to {{getOrderByStatement()}} is made. It seems to work against my test case and the test cases in the repository. I'll post my patch momentarily. This bug was first mentioned in the comments in DC-313, but the original ticket comes across as more of a feature request for the {{hasMany()}} {{orderBy}} feature. |
| Attachment | DC651TestCase.php [ 10585 ] |
| Attachment | Ticket_DC651.patch [ 10586 ] |
| Summary | Doctrine_Record::option('orderBy', ...) of join's right side being applied to refTable in m2m relationship | [PATCH] Doctrine_Record::option('orderBy', ...) of join's right side being applied to refTable in m2m relationship |
| Attachment | Query_orderBy_relation.diff [ 10751 ] |
| Attachment | Ticket_DC651.patch [ 10586 ] |
| Attachment | Ticket_DC651.patch [ 10806 ] |
| Affects Version/s | 1.2.3 [ 10051 ] | |
| Fix Version/s | 1.2.4 [ 10063 ] | |
| Fix Version/s | 1.2.3 [ 10051 ] | |
| Fix Version/s | 1.2.2 [ 10047 ] |