diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index b58bfb933..e1d8cd1f2 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -210,6 +210,7 @@ abstract class AbstractHydrator case (isset($this->_rsm->scalarMappings[$key])): $cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key]; + $cache[$key]['type'] = Type::getType($this->_rsm->typeMappings[$key]); $cache[$key]['isScalar'] = true; break; @@ -232,6 +233,8 @@ abstract class AbstractHydrator } if (isset($cache[$key]['isScalar'])) { + $value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform); + $rowData['scalars'][$cache[$key]['fieldName']] = $value; continue; diff --git a/lib/Doctrine/ORM/Query/ResultSetMapping.php b/lib/Doctrine/ORM/Query/ResultSetMapping.php index b35af474f..32aa298f1 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMapping.php +++ b/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -71,6 +71,12 @@ class ResultSetMapping */ public $scalarMappings = array(); + /** + * @ignore + * @var array Maps column names in the result set to the alias/field type to use in the mapped result. + */ + public $typeMappings = array(); + /** * @ignore * @var array Maps entities in the result set to the alias name to use in the mapped result. @@ -296,12 +302,16 @@ class ResultSetMapping * * @param string $columnName The name of the column in the SQL result set. * @param string $alias The result alias with which the scalar result should be placed in the result structure. + * @param string $type The column type + * * @return ResultSetMapping This ResultSetMapping instance. + * * @todo Rename: addScalar */ - public function addScalarResult($columnName, $alias) + public function addScalarResult($columnName, $alias, $type = null) { $this->scalarMappings[$columnName] = $alias; + $this->typeMappings[$columnName] = $type ?: 'string'; if ( ! $this->isMixed && $this->fieldMappings) { $this->isMixed = true; diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 47263517c..f1ef94ff1 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1084,8 +1084,10 @@ class SqlWalker implements TreeWalker $col = $sqlTableAlias . '.' . $columnName; + $fieldType = $class->getTypeOfField($fieldName); + if (isset($class->fieldMappings[$fieldName]['requireSQLConversion'])) { - $type = Type::getType($class->getTypeOfField($fieldName)); + $type = Type::getType($fieldType); $col = $type->convertToPHPValueSQL($col, $this->_conn->getDatabasePlatform()); } @@ -1094,7 +1096,7 @@ class SqlWalker implements TreeWalker $this->_scalarResultAliasMap[$resultAlias] = $columnAlias; if ( ! $hidden) { - $this->_rsm->addScalarResult($columnAlias, $resultAlias); + $this->_rsm->addScalarResult($columnAlias, $resultAlias, $fieldType); $this->_scalarFields[$dqlAlias][$fieldName] = $columnAlias; } break; @@ -1118,7 +1120,8 @@ class SqlWalker implements TreeWalker $this->_scalarResultAliasMap[$resultAlias] = $columnAlias; if ( ! $hidden) { - $this->_rsm->addScalarResult($columnAlias, $resultAlias); + // We cannot resolve field type here; assume 'string'. + $this->_rsm->addScalarResult($columnAlias, $resultAlias, 'string'); } break; @@ -1131,7 +1134,8 @@ class SqlWalker implements TreeWalker $this->_scalarResultAliasMap[$resultAlias] = $columnAlias; if ( ! $hidden) { - $this->_rsm->addScalarResult($columnAlias, $resultAlias); + // We cannot resolve field type here; assume 'string'. + $this->_rsm->addScalarResult($columnAlias, $resultAlias, 'string'); } break; diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC657Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC657Test.php new file mode 100644 index 000000000..a0a10a1b7 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC657Test.php @@ -0,0 +1,118 @@ +useModelSet('generic'); + parent::setUp(); + + $this->loadFixtures(); + } + + public function testEntitySingleResult() + { + $query = $this->_em->createQuery('SELECT d FROM ' . self::NS . '\DateTimeModel d'); + $datetime = $query->setMaxResults(1)->getSingleResult(); + + $this->assertTrue($datetime instanceof DateTimeModel); + + $this->assertTrue($datetime->datetime instanceof \DateTime); + $this->assertTrue($datetime->time instanceof \DateTime); + $this->assertTrue($datetime->date instanceof \DateTime); + } + + public function testScalarResult() + { + $query = $this->_em->createQuery('SELECT d.id, d.time, d.date, d.datetime FROM ' . self::NS . '\DateTimeModel d ORDER BY d.date ASC'); + $result = $query->getScalarResult(); + + $this->assertCount(2,$result); + + $this->assertEquals('11:11:11', $result[0]['time']); + $this->assertEquals('2010-01-01', $result[0]['date']); + $this->assertEquals('2010-01-01 11:11:11', $result[0]['datetime']); + + $this->assertEquals('12:12:12', $result[1]['time']); + $this->assertEquals('2010-02-02', $result[1]['date']); + $this->assertEquals('2010-02-02 12:12:12', $result[1]['datetime']); + } + + public function testaTicketEntityArrayResult() + { + $query = $this->_em->createQuery('SELECT d FROM ' . self::NS . '\DateTimeModel d ORDER BY d.date ASC'); + $result = $query->getArrayResult(); + + $this->assertCount(2,$result); + + $this->assertTrue($result[0]['datetime'] instanceof \DateTime); + $this->assertTrue($result[0]['time'] instanceof \DateTime); + $this->assertTrue($result[0]['date'] instanceof \DateTime); + + $this->assertTrue($result[1]['datetime'] instanceof \DateTime); + $this->assertTrue($result[1]['time'] instanceof \DateTime); + $this->assertTrue($result[1]['date'] instanceof \DateTime); + } + + public function testTicketSingleResult() + { + $query = $this->_em->createQuery('SELECT d.id, d.time, d.date, d.datetime FROM ' . self::NS . '\DateTimeModel d ORDER BY d.date ASC'); + $datetime = $query->setMaxResults(1)->getSingleResult(); + + $this->assertTrue(is_array($datetime)); + + $this->assertTrue($datetime['datetime'] instanceof \DateTime); + $this->assertTrue($datetime['time'] instanceof \DateTime); + $this->assertTrue($datetime['date'] instanceof \DateTime); + } + + public function testTicketResult() + { + $query = $this->_em->createQuery('SELECT d.id, d.time, d.date, d.datetime FROM ' . self::NS . '\DateTimeModel d ORDER BY d.date ASC'); + $result = $query->getResult(); + + $this->assertCount(2,$result); + + $this->assertTrue($result[0]['time'] instanceof \DateTime); + $this->assertTrue($result[0]['date'] instanceof \DateTime); + $this->assertTrue($result[0]['datetime'] instanceof \DateTime); + $this->assertEquals('2010-01-01 11:11:11', $result[0]['datetime']->format('Y-m-d G:i:s')); + + $this->assertTrue($result[1]['time'] instanceof \DateTime); + $this->assertTrue($result[1]['date'] instanceof \DateTime); + $this->assertTrue($result[1]['datetime'] instanceof \DateTime); + $this->assertEquals('2010-02-02 12:12:12', $result[1]['datetime']->format('Y-m-d G:i:s')); + } + + public function loadFixtures() + { + $timezone = new \DateTimeZone('America/Sao_Paulo'); + + $dateTime1 = new DateTimeModel(); + $dateTime2 = new DateTimeModel(); + + $dateTime1->date = new \DateTime('2010-01-01', $timezone); + $dateTime1->time = new \DateTime('2010-01-01 11:11:11', $timezone); + $dateTime1->datetime= new \DateTime('2010-01-01 11:11:11', $timezone); + + $dateTime2->date = new \DateTime('2010-02-02', $timezone); + $dateTime2->time = new \DateTime('2010-02-02 12:12:12', $timezone); + $dateTime2->datetime= new \DateTime('2010-02-02 12:12:12', $timezone); + + $this->_em->persist($dateTime1); + $this->_em->persist($dateTime2); + + $this->_em->flush(); + } +} \ No newline at end of file