Skip to content

Commit

Permalink
Merge pull request #690 from FabioBatSilva/DDC-2494
Browse files Browse the repository at this point in the history
[DDC-2494] Apply type conversion to meta columns
  • Loading branch information
guilhermeblanco committed Jun 12, 2013
2 parents b15758b + c1e688f commit 6937061
Show file tree
Hide file tree
Showing 4 changed files with 256 additions and 43 deletions.
63 changes: 27 additions & 36 deletions lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php
Expand Up @@ -114,9 +114,8 @@ protected function hydrateRowData(array $sqlResult, array &$cache, array &$resul
}

// Convert field to a valid PHP value
if (isset($cache[$column]['field'])) {
$type = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type']);
$value = $type->convertToPHPValue($value, $this->_platform);
if (isset($cache[$column]['type'])) {
$value = Type::getType($cache[$column]['type'])->convertToPHPValue($value, $this->_platform);
}

// Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
Expand Down Expand Up @@ -145,44 +144,36 @@ protected function hydrateRowData(array $sqlResult, array &$cache, array &$resul
*/
protected function hydrateColumnInfo($entityName, $column)
{
switch (true) {
case (isset($this->_rsm->fieldMappings[$column])):
$class = isset($this->declaringClasses[$column])
? $this->declaringClasses[$column]
: $this->class;

// If class is not part of the inheritance, ignore
if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) {
return null;
}

return array(
'class' => $class,
'name' => $this->_rsm->fieldMappings[$column],
'field' => true,
);

case (isset($this->_rsm->relationMap[$column])):
$class = isset($this->_rsm->relationMap[$column])
? $this->_rsm->relationMap[$column]
: $this->class;
if (isset($this->_rsm->fieldMappings[$column])) {
$name = $this->_rsm->fieldMappings[$column];
$class = isset($this->declaringClasses[$column])
? $this->declaringClasses[$column]
: $this->class;

// If class is not self referencing, ignore
if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) {
return null;
}
// If class is not part of the inheritance, ignore
if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) {
return null;
}

// TODO: Decide what to do with associations. It seems original code is incomplete.
// One solution is to load the association, but it might require extra efforts.
return array('name' => $column);
return array(
'name' => $name,
'type' => $class->fieldMappings[$name]['type']
);
}

case (isset($this->_rsm->metaMappings[$column])):
return array(
'name' => $this->_rsm->metaMappings[$column]
);
if (isset($this->_rsm->metaMappings[$column])) {
return array(
'name' => $this->_rsm->metaMappings[$column],
'type' => (isset($this->_rsm->typeMappings[$column]) ? $this->_rsm->typeMappings[$column] : null)
);
}

default:
return null;
// An ObjectHydrator should be used instead of SimpleObjectHydrator
if (isset($this->_rsm->relationMap[$column])) {
throw new \Exception(sprintf('Unable to retrieve association information for column "%s"', $column));
}

return null;
}
}
12 changes: 9 additions & 3 deletions lib/Doctrine/ORM/Persisters/BasicEntityPersister.php
Expand Up @@ -1334,16 +1334,22 @@ protected function getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $
return '';
}

$columnList = array();
$columnList = array();
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);

foreach ($assoc['joinColumns'] as $joinColumn) {

$type = null;
$isIdentifier = isset($assoc['id']) && $assoc['id'] === true;
$quotedColumn = $this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform);
$resultColumnName = $this->getSQLColumnAlias($joinColumn['name']);
$columnList[] = $this->getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) )
. '.' . $quotedColumn . ' AS ' . $resultColumnName;

$this->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, isset($assoc['id']) && $assoc['id'] === true);
if (isset($targetClass->fieldNames[$joinColumn['referencedColumnName']])) {
$type = $targetClass->fieldMappings[$targetClass->fieldNames[$joinColumn['referencedColumnName']]]['type'];
}

$this->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, $isIdentifier, $type);
}

return implode(', ', $columnList);
Expand Down
13 changes: 9 additions & 4 deletions lib/Doctrine/ORM/Query/ResultSetMapping.php
Expand Up @@ -543,14 +543,15 @@ public function isMixedResult()
/**
* Adds a meta column (foreign key or discriminator column) to the result set.
*
* @param string $alias
* @param string $columnName
* @param string $fieldName
* @param string $alias The result alias with which the meta result should be placed in the result structure.
* @param string $columnName The name of the column in the SQL result set.
* @param string $fieldName The name of the field on the declaring class.
* @param bool $isIdentifierColumn
* @param string $type The column type
*
* @return ResultSetMapping This ResultSetMapping instance.
*/
public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false)
public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColumn = false, $type = null)
{
$this->metaMappings[$columnName] = $fieldName;
$this->columnOwnerMap[$columnName] = $alias;
Expand All @@ -559,6 +560,10 @@ public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColu
$this->isIdentifierColumn[$alias][$columnName] = true;
}

if ($type) {
$this->typeMappings[$columnName] = $type;
}

return $this;
}
}
Expand Down
211 changes: 211 additions & 0 deletions tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2494Test.php
@@ -0,0 +1,211 @@
<?php

namespace Doctrine\Tests\ORM\Functional\Ticket;

use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;

/**
* @group DDC-2494
*/
class DDC2494Test extends \Doctrine\Tests\OrmFunctionalTestCase
{
protected function setUp()
{
parent::setUp();

DDC2494TinyIntType::$calls = array();

Type::addType('ddc2494_tinyint', __NAMESPACE__ . '\DDC2494TinyIntType');

$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata(DDC2494Currency::CLASSNAME),
$this->_em->getClassMetadata(DDC2494Campaign::CLASSNAME),
));
}

public function testIssue()
{
$currency = new DDC2494Currency(1, 2);

$this->_em->persist($currency);
$this->_em->flush();

$campaign = new DDC2494Campaign($currency);

$this->_em->persist($campaign);
$this->_em->flush();
$this->_em->close();

$this->assertArrayHasKey('convertToDatabaseValue', DDC2494TinyIntType::$calls);
$this->assertCount(3, DDC2494TinyIntType::$calls['convertToDatabaseValue']);

$item = $this->_em->find(DDC2494Campaign::CLASSNAME, $campaign->getId());

$this->assertInstanceOf(DDC2494Campaign::CLASSNAME, $item);
$this->assertInstanceOf(DDC2494Currency::CLASSNAME, $item->getCurrency());

$queryCount = $this->getCurrentQueryCount();

$this->assertInstanceOf('\Doctrine\Common\Proxy\Proxy', $item->getCurrency());
$this->assertFalse($item->getCurrency()->__isInitialized());

$this->assertArrayHasKey('convertToPHPValue', DDC2494TinyIntType::$calls);
$this->assertCount(1, DDC2494TinyIntType::$calls['convertToPHPValue']);

$this->assertInternalType('integer', $item->getCurrency()->getId());
$this->assertCount(1, DDC2494TinyIntType::$calls['convertToPHPValue']);
$this->assertFalse($item->getCurrency()->__isInitialized());

$this->assertEquals($queryCount, $this->getCurrentQueryCount());

$this->assertInternalType('integer', $item->getCurrency()->getTemp());
$this->assertCount(3, DDC2494TinyIntType::$calls['convertToPHPValue']);
$this->assertTrue($item->getCurrency()->__isInitialized());

$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
}
}

/**
* @Table(name="ddc2494_currency")
* @Entity
*/
class DDC2494Currency
{
const CLASSNAME = __CLASS__;

/**
* @Id
* @Column(type="integer", type="ddc2494_tinyint")
*/
protected $id;

/**
* @Column(name="temp", type="ddc2494_tinyint", nullable=false)
*/
protected $temp;

/**
* @var \Doctrine\Common\Collections\Collection
*
* @OneToMany(targetEntity="DDC2494Campaign", mappedBy="currency")
*/
protected $campaigns;

public function __construct($id, $temp)
{
$this->id = $id;
$this->temp = $temp;
}

public function getId()
{
return $this->id;
}

public function getTemp()
{
return $this->temp;
}

public function getCampaigns()
{
return $this->campaigns;
}
}

/**
* @Table(name="ddc2494_campaign")
* @Entity
*/
class DDC2494Campaign
{
const CLASSNAME = __CLASS__;

/**
* @Id
* @GeneratedValue
* @Column(type="integer")
*/
protected $id;

/**
* @var \Doctrine\Tests\ORM\Functional\Ticket\DDC2494Currency
*
* @ManyToOne(targetEntity="DDC2494Currency", inversedBy="campaigns")
* @JoinColumn(name="currency_id", referencedColumnName="id", nullable=false)
*/
protected $currency;

public function __construct(DDC2494Currency $currency)
{
$this->currency = $currency;
}

public function getId()
{
return $this->id;
}

/**
* @return \Doctrine\Tests\ORM\Functional\Ticket\DDC2494Currency
*/
public function getCurrency()
{
return $this->currency;
}
}

class DDC2494TinyIntType extends Type
{
public static $calls = array();

/**
* {@inheritdoc}
*/
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
{
return $platform->getSmallIntTypeDeclarationSQL($fieldDeclaration);
}

/**
* {@inheritdoc}
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
$return = (string) $value;

self::$calls[__FUNCTION__][] = array(
'value' => $value,
'return' => $return,
'platform' => $platform,
);

return $return;
}

/**
* {@inheritdoc}
*/
public function convertToPHPValue($value, AbstractPlatform $platform)
{
$return = (integer) $value;

self::$calls[__FUNCTION__][] = array(
'value' => $value,
'return' => $return,
'platform' => $platform,
);

return $return;
}

/**
* {@inheritdoc}
*/
public function getName()
{
return 'ddc2494_tinyint';
}
}

0 comments on commit 6937061

Please sign in to comment.