From e5174af669fee1e118a6fd75a2566529f888aeef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sun, 11 Jun 2023 15:33:33 +0200 Subject: [PATCH] Document how to produce DTOs with a result set mapping --- docs/en/reference/native-sql.rst | 34 +++++++++ .../Tests/ORM/Functional/NativeQueryTest.php | 72 +++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/docs/en/reference/native-sql.rst b/docs/en/reference/native-sql.rst index 434423bf5b8..97d92dd3861 100644 --- a/docs/en/reference/native-sql.rst +++ b/docs/en/reference/native-sql.rst @@ -250,6 +250,40 @@ The first parameter is the name of the column in the SQL result set and the second parameter is the result alias under which the value of the column will be placed in the transformed Doctrine result. +Special case: DTOs +................... + +You can also use ``ResultSetMapping`` to map the results of a native SQL +query to a DTO (Data Transfer Object). This is done by adding scalar +results for each argument of the DTO's constructor, then filling the +``newObjectMappings`` property of the ``ResultSetMapping`` with +information about where to map each scalar result: + +.. code-block:: php + + addScalarResult('name', 1, 'string'); + $rsm->addScalarResult('email', 2, 'string'); + $rsm->addScalarResult('city', 3, 'string'); + $rsm->newObjectMappings['name'] = [ + 'className' => CmsUserDTO::class, + 'objIndex' => 0, // a result can contain many DTOs, this is the index of the DTO to map to + 'argIndex' => 0, // each scalar result can be mapped to a different argument of the DTO constructor + ]; + $rsm->newObjectMappings['email'] = [ + 'className' => CmsUserDTO::class, + 'objIndex' => 0, + 'argIndex' => 1, + ]; + $rsm->newObjectMappings['city'] = [ + 'className' => CmsUserDTO::class, + 'objIndex' => 0, + 'argIndex' => 2, + ]; + + Meta results ~~~~~~~~~~~~ diff --git a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php index 8597b2f088c..521393e9692 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php @@ -19,6 +19,7 @@ use Doctrine\Tests\Models\CMS\CmsEmail; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; +use Doctrine\Tests\Models\CMS\CmsUserDTO; use Doctrine\Tests\Models\Company\CompanyContract; use Doctrine\Tests\Models\Company\CompanyEmployee; use Doctrine\Tests\Models\Company\CompanyFixContract; @@ -155,6 +156,77 @@ public function testJoinedOneToManyNativeQuery(): void self::assertSame($phones[0]->getUser(), $users[0]); } + public function testMappingAsDto(): void + { + $user = new CmsUser(); + $user->name = 'Roman'; + $user->username = 'romanb'; + $user->status = 'dev'; + + $phone = new CmsPhonenumber(); + $phone->phonenumber = 424242; + + $user->addPhonenumber($phone); + + $email = new CmsEmail(); + $email->email = 'fabio.bat.silva@gmail.com'; + + $user->setEmail($email); + + $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(); + $rsm->addScalarResult('name', 1, 'string'); + $rsm->addScalarResult('email', 2, 'string'); + $rsm->addScalarResult('city', 3, 'string'); + $rsm->newObjectMappings['name'] = [ + 'className' => CmsUserDTO::class, + 'objIndex' => 0, + 'argIndex' => 0, + ]; + $rsm->newObjectMappings['email'] = [ + 'className' => CmsUserDTO::class, + 'objIndex' => 0, + 'argIndex' => 1, + ]; + $rsm->newObjectMappings['city'] = [ + 'className' => CmsUserDTO::class, + 'objIndex' => 0, + 'argIndex' => 2, + ]; + $query = $this->_em->createNativeQuery( + <<<'SQL' + SELECT u.name, e.email, a.city + FROM cms_users u +INNER JOIN cms_phonenumbers p ON u.id = p.user_id +INNER JOIN cms_emails e ON e.id = u.email_id +INNER JOIN cms_addresses a ON u.id = a.user_id + WHERE username = ? +SQL + , + $rsm + ); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + self::assertCount(1, $users); + $user = $users[0]; + self::assertInstanceOf(CmsUserDTO::class, $user); + self::assertEquals('Roman', $user->name); + self::assertEquals('fabio.bat.silva@gmail.com', $user->email); + self::assertEquals('Berlin', $user->address); + } + public function testJoinedOneToOneNativeQuery(): void { $user = new CmsUser();