From 20dc72ef9ae0d7c4afead35c249c224b95570aa7 Mon Sep 17 00:00:00 2001 From: Michael Ridgway Date: Tue, 29 Mar 2011 20:35:01 -0400 Subject: [PATCH 1/4] First pass on RSM helper functions for adding entities --- lib/Doctrine/ORM/Query/ResultSetMapping.php | 38 ++++++++- .../Tests/ORM/Functional/NativeQueryTest.php | 81 +++++++++++++++++++ 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Query/ResultSetMapping.php b/lib/Doctrine/ORM/Query/ResultSetMapping.php index 7066405df..ededf1153 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMapping.php +++ b/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -1,7 +1,5 @@ metaMappings[$columnName] = $fieldName; $this->columnOwnerMap[$columnName] = $alias; } + + /** + * Adds a root entity and all of its fields to the result set. + * + * @param ClassMetadata $classMetadata + * @param string $alias The unique alias to use for the root entity. + * @param string $prefix Prefix for columns + */ + public function addRootEntityFromClassMetadata(ClassMetadata $classMetadata, $alias, $prefix = '') + { + $this->addEntityResult($classMetadata->getReflectionClass()->getName(), $alias); + foreach ($classMetadata->getColumnNames() AS $columnName) { + // $columnName should use DBAL\Platforms\AbstractPlatform::getSQLResultCasing() but we don't have the EM + $this->addFieldResult($alias, $prefix . $columnName, $classMetadata->getFieldName($columnName)); + } + } + + /** + * Adds a joined entity and all of its fields to the result set. + * + * @param ClassMetadata $classMetadata + * @param string $alias The unique alias to use for the joined entity. + * @param string $parentAlias The alias of the entity result that is the parent of this joined result. + * @param object $relation The association field that connects the parent entity result with the joined entity result. + * @param string $prefix Prefix for columns + */ + public function addJoinedEntityFromClassMetadata(ClassMetadata $classMetadata, $alias, $parentAlias, $relation, $prefix = '') + { + $this->addJoinedEntityResult($classMetadata->getReflectionClass()->getName(), $alias, $parentAlias, $relation); + foreach ($classMetadata->getColumnNames() AS $columnName) { + // $columnName should use DBAL\Platforms\AbstractPlatform::getSQLResultCasing() but we don't have the EM + $this->addFieldResult($alias, $prefix . $columnName, $classMetadata->getFieldName($columnName)); + } + } } diff --git a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php index 23dfa3a73..65778badf 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php @@ -156,5 +156,86 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertSame($q, $q2); } + + public function testJoinedOneToManyNativeQueryWithRSMHelper() + { + $user = new CmsUser; + $user->name = 'Roman'; + $user->username = 'romanb'; + $user->status = 'dev'; + + $phone = new CmsPhonenumber; + $phone->phonenumber = 424242; + + $user->addPhonenumber($phone); + + $this->_em->persist($user); + $this->_em->flush(); + + $this->_em->clear(); + + $rsm = new ResultSetMapping; + $userMetadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $phoneMetadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'); + $rsm->addRootEntityFromClassMetadata($userMetadata, 'u'); + $rsm->addJoinedEntityFromClassMetadata($phoneMetadata, 'p', 'u', 'phonenumbers', 'p_'); + $query = $this->_em->createNativeQuery('SELECT u.*, p.phonenumber AS p_phonenumber FROM cms_users u INNER JOIN cms_phonenumbers p ON u.id = p.user_id WHERE username = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + $this->assertEquals(1, count($users)); + $this->assertTrue($users[0] instanceof CmsUser); + $this->assertEquals('Roman', $users[0]->name); + $this->assertTrue($users[0]->getPhonenumbers() instanceof \Doctrine\ORM\PersistentCollection); + $this->assertTrue($users[0]->getPhonenumbers()->isInitialized()); + $this->assertEquals(1, count($users[0]->getPhonenumbers())); + $phones = $users[0]->getPhonenumbers(); + $this->assertEquals(424242, $phones[0]->phonenumber); + $this->assertTrue($phones[0]->getUser() === $users[0]); + } + + public function testJoinedOneToOneNativeQueryWithRSMHelper() + { + $user = new CmsUser; + $user->name = 'Roman'; + $user->username = 'romanb'; + $user->status = 'dev'; + + $addr = new CmsAddress; + $addr->country = 'germany'; + $addr->zip = 10827; + $addr->city = 'Berlin'; + + + $user->setAddress($addr); + + $this->_em->persist($user); + $this->_em->flush(); + + $this->_em->clear(); + + + $rsm = new ResultSetMapping; + $userMetadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); + $addressMetadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'); + $rsm->addRootEntityFromClassMetadata($userMetadata, 'u'); + $rsm->addJoinedEntityFromClassMetadata($addressMetadata, 'a', 'u', 'address', 'a_'); + + $query = $this->_em->createNativeQuery('SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', $rsm); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + + $this->assertEquals(1, count($users)); + $this->assertTrue($users[0] instanceof CmsUser); + $this->assertEquals('Roman', $users[0]->name); + $this->assertTrue($users[0]->getPhonenumbers() instanceof \Doctrine\ORM\PersistentCollection); + $this->assertFalse($users[0]->getPhonenumbers()->isInitialized()); + $this->assertTrue($users[0]->getAddress() instanceof CmsAddress); + $this->assertTrue($users[0]->getAddress()->getUser() == $users[0]); + $this->assertEquals('germany', $users[0]->getAddress()->getCountry()); + $this->assertEquals(10827, $users[0]->getAddress()->getZipCode()); + $this->assertEquals('Berlin', $users[0]->getAddress()->getCity()); + } } From c46d83514631841800c782890ad27a2b77de200f Mon Sep 17 00:00:00 2001 From: Michael Ridgway Date: Wed, 30 Mar 2011 10:27:31 -0400 Subject: [PATCH 2/4] Moved new functions to ResultSetMappingBuilder class --- lib/Doctrine/ORM/Query/ResultSetMapping.php | 37 +-------- .../ORM/Query/ResultSetMappingBuilder.php | 80 +++++++++++++++++++ .../Tests/ORM/Functional/NativeQueryTest.php | 21 +++-- 3 files changed, 90 insertions(+), 48 deletions(-) create mode 100644 lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php diff --git a/lib/Doctrine/ORM/Query/ResultSetMapping.php b/lib/Doctrine/ORM/Query/ResultSetMapping.php index ededf1153..1de2d4459 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMapping.php +++ b/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -392,39 +392,4 @@ class ResultSetMapping $this->metaMappings[$columnName] = $fieldName; $this->columnOwnerMap[$columnName] = $alias; } - - /** - * Adds a root entity and all of its fields to the result set. - * - * @param ClassMetadata $classMetadata - * @param string $alias The unique alias to use for the root entity. - * @param string $prefix Prefix for columns - */ - public function addRootEntityFromClassMetadata(ClassMetadata $classMetadata, $alias, $prefix = '') - { - $this->addEntityResult($classMetadata->getReflectionClass()->getName(), $alias); - foreach ($classMetadata->getColumnNames() AS $columnName) { - // $columnName should use DBAL\Platforms\AbstractPlatform::getSQLResultCasing() but we don't have the EM - $this->addFieldResult($alias, $prefix . $columnName, $classMetadata->getFieldName($columnName)); - } - } - - /** - * Adds a joined entity and all of its fields to the result set. - * - * @param ClassMetadata $classMetadata - * @param string $alias The unique alias to use for the joined entity. - * @param string $parentAlias The alias of the entity result that is the parent of this joined result. - * @param object $relation The association field that connects the parent entity result with the joined entity result. - * @param string $prefix Prefix for columns - */ - public function addJoinedEntityFromClassMetadata(ClassMetadata $classMetadata, $alias, $parentAlias, $relation, $prefix = '') - { - $this->addJoinedEntityResult($classMetadata->getReflectionClass()->getName(), $alias, $parentAlias, $relation); - foreach ($classMetadata->getColumnNames() AS $columnName) { - // $columnName should use DBAL\Platforms\AbstractPlatform::getSQLResultCasing() but we don't have the EM - $this->addFieldResult($alias, $prefix . $columnName, $classMetadata->getFieldName($columnName)); - } - } -} - +} \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php b/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php new file mode 100644 index 000000000..517fd0036 --- /dev/null +++ b/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php @@ -0,0 +1,80 @@ +. + */ + +namespace Doctrine\ORM\Query; + +use Doctrine\ORM\EntityManager; + +/** + * A ResultSetMappingBuilder uses the EntityManager to automatically populate entity fields + * + * @author Michael Ridgway + * @since 2.1 + */ +class ResultSetMappingBuilder extends ResultSetMapping +{ + /** + * @var EntityManager + */ + private $em; + + /** + * @param EntityManager + */ + public function __construct(EntityManager $em) + { + $this->em = $em; + } + + /** + * Adds a root entity and all of its fields to the result set. + * + * @param string $class The class name of the root entity. + * @param string $alias The unique alias to use for the root entity. + * @param string $prefix Prefix for columns + */ + public function addRootEntityFromClassMetadata($class, $alias, $prefix = '') + { + $this->addEntityResult($class, $alias); + $classMetadata = $this->em->getClassMetadata($class); + $platform = $this->em->getConnection()->getDatabasePlatform(); + foreach ($classMetadata->getColumnNames() AS $columnName) { + $this->addFieldResult($alias, $platform->getSQLResultCasing($prefix . $columnName), $classMetadata->getFieldName($columnName)); + } + } + + /** + * Adds a joined entity and all of its fields to the result set. + * + * @param string $class The class name of the joined entity. + * @param string $alias The unique alias to use for the joined entity. + * @param string $parentAlias The alias of the entity result that is the parent of this joined result. + * @param object $relation The association field that connects the parent entity result with the joined entity result. + * @param string $prefix Prefix for columns + */ + public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $prefix = '') + { + $this->addJoinedEntityResult($class, $alias, $parentAlias, $relation); + $classMetadata = $this->em->getClassMetadata($class); + $platform = $this->em->getConnection()->getDatabasePlatform(); + foreach ($classMetadata->getColumnNames() AS $columnName) { + $this->addFieldResult($alias, $platform->getSQLResultCasing($prefix . $columnName), $classMetadata->getFieldName($columnName)); + } + } +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php index 65778badf..1b1a713b8 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php @@ -3,6 +3,7 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsAddress; @@ -157,7 +158,7 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertSame($q, $q2); } - public function testJoinedOneToManyNativeQueryWithRSMHelper() + public function testJoinedOneToManyNativeQueryWithRSMBuilder() { $user = new CmsUser; $user->name = 'Roman'; @@ -174,11 +175,9 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->clear(); - $rsm = new ResultSetMapping; - $userMetadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); - $phoneMetadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'); - $rsm->addRootEntityFromClassMetadata($userMetadata, 'u'); - $rsm->addJoinedEntityFromClassMetadata($phoneMetadata, 'p', 'u', 'phonenumbers', 'p_'); + $rsm = new ResultSetMappingBuilder($this->_em); + $rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsUser', 'u'); + $rsm->addJoinedEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', 'phonenumbers', 'p_'); $query = $this->_em->createNativeQuery('SELECT u.*, p.phonenumber AS p_phonenumber FROM cms_users u INNER JOIN cms_phonenumbers p ON u.id = p.user_id WHERE username = ?', $rsm); $query->setParameter(1, 'romanb'); @@ -194,7 +193,7 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertTrue($phones[0]->getUser() === $users[0]); } - public function testJoinedOneToOneNativeQueryWithRSMHelper() + public function testJoinedOneToOneNativeQueryWithRSMBuilder() { $user = new CmsUser; $user->name = 'Roman'; @@ -215,11 +214,9 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->_em->clear(); - $rsm = new ResultSetMapping; - $userMetadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'); - $addressMetadata = $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'); - $rsm->addRootEntityFromClassMetadata($userMetadata, 'u'); - $rsm->addJoinedEntityFromClassMetadata($addressMetadata, 'a', 'u', 'address', 'a_'); + $rsm = new ResultSetMappingBuilder($this->_em); + $rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsUser', 'u'); + $rsm->addJoinedEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress', 'a', 'u', 'address', 'a_'); $query = $this->_em->createNativeQuery('SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', $rsm); $query->setParameter(1, 'romanb'); From b1b17376fffb80aac82ff7631b52457b2ee3e09a Mon Sep 17 00:00:00 2001 From: Michael Ridgway Date: Thu, 31 Mar 2011 17:22:13 -0400 Subject: [PATCH 3/4] Removing left over class import --- lib/Doctrine/ORM/Query/ResultSetMapping.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/Doctrine/ORM/Query/ResultSetMapping.php b/lib/Doctrine/ORM/Query/ResultSetMapping.php index 1de2d4459..b10b0d15d 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMapping.php +++ b/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -19,8 +19,6 @@ namespace Doctrine\ORM\Query; -use Doctrine\ORM\Mapping\ClassMetadata; - /** * A ResultSetMapping describes how a result set of an SQL query maps to a Doctrine result. * From af4cf0d0ba8cd5d38689db8b9063ef8d8d1ae8d7 Mon Sep 17 00:00:00 2001 From: Michael Ridgway Date: Thu, 14 Apr 2011 20:55:03 -0400 Subject: [PATCH 4/4] Replaced prefix parameter with renamedColumns; Added exception when duplicate columns found --- .../ORM/Query/ResultSetMappingBuilder.php | 32 +++++++++++++++---- .../Tests/ORM/Functional/NativeQueryTest.php | 20 +++++++++--- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php b/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php index 517fd0036..ceacc9638 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php +++ b/lib/Doctrine/ORM/Query/ResultSetMappingBuilder.php @@ -47,15 +47,25 @@ class ResultSetMappingBuilder extends ResultSetMapping * * @param string $class The class name of the root entity. * @param string $alias The unique alias to use for the root entity. - * @param string $prefix Prefix for columns + * @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName) */ - public function addRootEntityFromClassMetadata($class, $alias, $prefix = '') + public function addRootEntityFromClassMetadata($class, $alias, $renamedColumns = array()) { $this->addEntityResult($class, $alias); $classMetadata = $this->em->getClassMetadata($class); + if ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()) { + throw new \InvalidArgumentException('ResultSetMapping builder does not currently support inheritance.'); + } $platform = $this->em->getConnection()->getDatabasePlatform(); foreach ($classMetadata->getColumnNames() AS $columnName) { - $this->addFieldResult($alias, $platform->getSQLResultCasing($prefix . $columnName), $classMetadata->getFieldName($columnName)); + $propertyName = $classMetadata->getFieldName($columnName); + if (isset($renamedColumns[$columnName])) { + $columnName = $renamedColumns[$columnName]; + } + if (isset($this->fieldMappings[$columnName])) { + throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper."); + } + $this->addFieldResult($alias, $platform->getSQLResultCasing($columnName), $propertyName); } } @@ -66,15 +76,25 @@ class ResultSetMappingBuilder extends ResultSetMapping * @param string $alias The unique alias to use for the joined entity. * @param string $parentAlias The alias of the entity result that is the parent of this joined result. * @param object $relation The association field that connects the parent entity result with the joined entity result. - * @param string $prefix Prefix for columns + * @param array $renamedColumns Columns that have been renamed (tableColumnName => queryColumnName) */ - public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $prefix = '') + public function addJoinedEntityFromClassMetadata($class, $alias, $parentAlias, $relation, $renamedColumns = array()) { $this->addJoinedEntityResult($class, $alias, $parentAlias, $relation); $classMetadata = $this->em->getClassMetadata($class); + if ($classMetadata->isInheritanceTypeSingleTable() || $classMetadata->isInheritanceTypeJoined()) { + throw new \InvalidArgumentException('ResultSetMapping builder does not currently support inheritance.'); + } $platform = $this->em->getConnection()->getDatabasePlatform(); foreach ($classMetadata->getColumnNames() AS $columnName) { - $this->addFieldResult($alias, $platform->getSQLResultCasing($prefix . $columnName), $classMetadata->getFieldName($columnName)); + $propertyName = $classMetadata->getFieldName($columnName); + if (isset($renamedColumns[$columnName])) { + $columnName = $renamedColumns[$columnName]; + } + if (isset($this->fieldMappings[$columnName])) { + throw new \InvalidArgumentException("The column '$columnName' conflicts with another column in the mapper."); + } + $this->addFieldResult($alias, $platform->getSQLResultCasing($columnName), $propertyName); } } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php index 1b1a713b8..2a09b8310 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php @@ -7,6 +7,8 @@ use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsAddress; +use Doctrine\Tests\Models\Company\CompanyFixContract; +use Doctrine\Tests\Models\Company\CompanyEmployee; require_once __DIR__ . '/../../TestInit.php'; @@ -177,8 +179,8 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $rsm = new ResultSetMappingBuilder($this->_em); $rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsUser', 'u'); - $rsm->addJoinedEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', 'phonenumbers', 'p_'); - $query = $this->_em->createNativeQuery('SELECT u.*, p.phonenumber AS p_phonenumber FROM cms_users u INNER JOIN cms_phonenumbers p ON u.id = p.user_id WHERE username = ?', $rsm); + $rsm->addJoinedEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', 'phonenumbers'); + $query = $this->_em->createNativeQuery('SELECT u.*, p.* FROM cms_users u LEFT JOIN cms_phonenumbers p ON u.id = p.user_id WHERE username = ?', $rsm); $query->setParameter(1, 'romanb'); $users = $query->getResult(); @@ -216,9 +218,9 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $rsm = new ResultSetMappingBuilder($this->_em); $rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsUser', 'u'); - $rsm->addJoinedEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress', 'a', 'u', 'address', 'a_'); + $rsm->addJoinedEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress', 'a', 'u', 'address', array('id' => 'a_id')); - $query = $this->_em->createNativeQuery('SELECT u.id, u.name, u.status, a.id AS a_id, a.country AS a_country, a.zip AS a_zip, a.city AS a_city FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', $rsm); + $query = $this->_em->createNativeQuery('SELECT u.*, a.*, a.id AS a_id FROM cms_users u INNER JOIN cms_addresses a ON u.id = a.user_id WHERE u.username = ?', $rsm); $query->setParameter(1, 'romanb'); $users = $query->getResult(); @@ -234,5 +236,15 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertEquals(10827, $users[0]->getAddress()->getZipCode()); $this->assertEquals('Berlin', $users[0]->getAddress()->getCity()); } + + /** + * @expectedException \InvalidArgumentException + */ + public function testRSMBuilderThrowsExceptionOnColumnConflict() + { + $rsm = new ResultSetMappingBuilder($this->_em); + $rsm->addRootEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsUser', 'u'); + $rsm->addJoinedEntityFromClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress', 'a', 'u', 'address'); + } }