Skip to content

Commit 66addcc

Browse files
authored
Merge pull request #655 from hkulekci/type-convertion-bug-fix-for-nullable-fields
Type convertion bug fix for nullable fields
2 parents 854c26e + 03b972d commit 66addcc

File tree

3 files changed

+152
-2
lines changed

3 files changed

+152
-2
lines changed

src/DoctrineModule/Stdlib/Hydrator/DoctrineObject.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,10 @@ public function hydrateValue($name, $value, $data = null)
272272
{
273273
$value = parent::hydrateValue($name, $value, $data);
274274

275+
if (is_null($value) && $this->isNullable($name)) {
276+
return null;
277+
}
278+
275279
return $this->handleTypeConversions($value, $this->metadata->getTypeOfField($name));
276280
}
277281

@@ -535,6 +539,10 @@ function ($item) {
535539
*/
536540
protected function handleTypeConversions($value, $typeOfField)
537541
{
542+
if (is_null($value)) {
543+
return null;
544+
}
545+
538546
switch ($typeOfField) {
539547
case 'boolean':
540548
$value = (bool)$value;
@@ -630,6 +638,26 @@ function ($value) {
630638
return false;
631639
}
632640

641+
/**
642+
* Check the field is nullable
643+
*
644+
* @param $name
645+
* @return bool
646+
*/
647+
private function isNullable($name)
648+
{
649+
//TODO: need update after updating isNullable method of Doctrine\ORM\Mapping\ClassMetadata
650+
if ($this->metadata->hasField($name)) {
651+
return method_exists($this->metadata, 'isNullable') && $this->metadata->isNullable($name);
652+
} else if ($this->metadata->hasAssociation($name) && method_exists($this->metadata, 'getAssociationMapping')) {
653+
$mapping = $this->metadata->getAssociationMapping($name);
654+
655+
return false !== $mapping && isset($mapping['nullable']) && $mapping['nullable'];
656+
}
657+
658+
return false;
659+
}
660+
633661
/**
634662
* Applies the naming strategy if there is one set
635663
*

tests/DoctrineModuleTest/Stdlib/Hydrator/DoctrineObjectTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,7 +1241,7 @@ public function testCanHydrateOneToOneAssociationByValueWithNullableRelation()
12411241

12421242
$data = ['toOne' => null];
12431243

1244-
$this->metadata->expects($this->once())
1244+
$this->metadata->expects($this->atLeastOnce())
12451245
->method('hasAssociation');
12461246

12471247
$object = $this->hydratorByValue->hydrate($data, $entity);
@@ -1255,7 +1255,7 @@ public function testCanHydrateOneToOneAssociationByReferenceWithNullableRelation
12551255

12561256
$this->configureObjectManagerForOneToOneEntity();
12571257
$this->objectManager->expects($this->never())->method('find');
1258-
$this->metadata->expects($this->once())->method('hasAssociation');
1258+
$this->metadata->expects($this->atLeastOnce())->method('hasAssociation');
12591259

12601260
$data = ['toOne' => null];
12611261

tests/DoctrineModuleTest/Stdlib/Hydrator/DoctrineObjectTypeConversionsTest.php

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,96 @@ function ($arg) use ($genericFieldType) {
119119
);
120120
}
121121

122+
public function configureObjectManagerForOneToOneEntity()
123+
{
124+
$refl = new ReflectionClass('DoctrineModuleTest\Stdlib\Hydrator\Asset\OneToOneEntity');
125+
126+
$this
127+
->metadata
128+
->expects($this->any())
129+
->method('getFieldNames')
130+
->will($this->returnValue(['id']));
131+
132+
$this
133+
->metadata
134+
->expects($this->any())
135+
->method('getAssociationNames')
136+
->will($this->returnValue(['toOne']));
137+
138+
$this
139+
->metadata
140+
->expects($this->any())
141+
->method('getTypeOfField')
142+
->with($this->logicalOr($this->equalTo('id'), $this->equalTo('toOne')))
143+
->will(
144+
$this->returnCallback(
145+
function ($arg) {
146+
if ($arg === 'id') {
147+
return 'integer';
148+
} elseif ($arg === 'toOne') {
149+
return 'DoctrineModuleTest\Stdlib\Hydrator\Asset\ByValueDifferentiatorEntity';
150+
}
151+
152+
throw new \InvalidArgumentException();
153+
}
154+
)
155+
);
156+
157+
$this
158+
->metadata
159+
->expects($this->any())
160+
->method('hasAssociation')
161+
->with($this->logicalOr($this->equalTo('id'), $this->equalTo('toOne')))
162+
->will(
163+
$this->returnCallback(
164+
function ($arg) {
165+
if ($arg === 'id') {
166+
return false;
167+
} elseif ($arg === 'toOne') {
168+
return true;
169+
}
170+
171+
throw new \InvalidArgumentException();
172+
}
173+
)
174+
);
175+
176+
$this
177+
->metadata
178+
->expects($this->any())
179+
->method('isSingleValuedAssociation')
180+
->with('toOne')
181+
->will($this->returnValue(true));
182+
183+
$this
184+
->metadata
185+
->expects($this->any())
186+
->method('getAssociationTargetClass')
187+
->with('toOne')
188+
->will($this->returnValue('DoctrineModuleTest\Stdlib\Hydrator\Asset\ByValueDifferentiatorEntity'));
189+
190+
$this
191+
->metadata
192+
->expects($this->any())
193+
->method('getReflectionClass')
194+
->will($this->returnValue($refl));
195+
196+
$this
197+
->metadata
198+
->expects($this->any())
199+
->method('getIdentifier')
200+
->will($this->returnValue(["id"]));
201+
202+
$this->hydratorByValue = new DoctrineObjectHydrator(
203+
$this->objectManager,
204+
true
205+
);
206+
$this->hydratorByReference = new DoctrineObjectHydrator(
207+
$this->objectManager,
208+
false
209+
);
210+
}
211+
122212
public function testHandleTypeConversionsDatetime()
123213
{
124214
// When using hydration by value, it will use the public API of the entity to set values (setters)
@@ -621,4 +711,36 @@ public function testHandleTypeConversionsDecimal()
621711
$this->assertTrue(is_string($entity->getGenericField()));
622712
$this->assertEquals('12345', $entity->getGenericField());
623713
}
714+
715+
public function testHandleTypeConversionsNullable()
716+
{
717+
// When using hydration by value, it will use the public API of the entity to set values (setters)
718+
$this->configureObjectManagerForSimpleEntityWithGenericField(null);
719+
720+
$entity = new Asset\SimpleEntityWithGenericField();
721+
$data = ['genericField' => null];
722+
723+
$entity = $this->hydratorByValue->hydrate($data, $entity);
724+
725+
$this->assertNull($entity->getGenericField());
726+
727+
$entity = new Asset\SimpleEntityWithGenericField();
728+
$data = ['genericField' => null];
729+
730+
$entity = $this->hydratorByReference->hydrate($data, $entity);
731+
732+
$this->assertNull($entity->getGenericField());
733+
}
734+
735+
public function testHandleTypeConversionsNullableForAssociatedFields()
736+
{
737+
$this->configureObjectManagerForOneToOneEntity();
738+
739+
$entity = new Asset\OneToOneEntity();
740+
$data = ['toOne' => null];
741+
742+
$entity = $this->hydratorByReference->hydrate($data, $entity);
743+
744+
$this->assertNull($entity->getToOne(false));
745+
}
624746
}

0 commit comments

Comments
 (0)