diff --git a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php index e9063c03660..f6c66b7f3dc 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php @@ -165,6 +165,10 @@ protected function hydrateRowData(array $row, array &$result) } } + if (isset($this->_hints[Query::HINT_REFRESH_ENTITY])) { + $this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data); + } + $uow = $this->_em->getUnitOfWork(); $entity = $uow->createEntity($entityName, $data, $this->_hints); diff --git a/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/lib/Doctrine/ORM/Proxy/ProxyFactory.php index 91a2a89521d..fe19c7d71e1 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyFactory.php +++ b/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -18,6 +18,7 @@ use Doctrine\ORM\Utility\IdentifierFlattener; use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\Proxy; +use ReflectionProperty; use Symfony\Component\VarExporter\ProxyHelper; use Symfony\Component\VarExporter\VarExporter; @@ -313,17 +314,24 @@ private function generateSkippedProperties(ClassMetadata $class): string { $skippedProperties = ['__isCloning' => true]; $identifiers = array_flip($class->getIdentifierFieldNames()); + $filter = ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE; + $reflector = $class->getReflectionClass(); - foreach ($class->getReflectionClass()->getProperties() as $property) { - $name = $property->getName(); + while ($reflector) { + foreach ($reflector->getProperties($filter) as $property) { + $name = $property->getName(); - if ($property->isStatic() || (($class->hasField($name) || $class->hasAssociation($name)) && ! isset($identifiers[$name]))) { - continue; - } + if ($property->isStatic() || (($class->hasField($name) || $class->hasAssociation($name)) && ! isset($identifiers[$name]))) { + continue; + } - $prefix = $property->isPrivate() ? "\0" . $property->getDeclaringClass()->getName() . "\0" : ($property->isProtected() ? "\0*\0" : ''); + $prefix = $property->isPrivate() ? "\0" . $property->getDeclaringClass()->getName() . "\0" : ($property->isProtected() ? "\0*\0" : ''); + + $skippedProperties[$prefix . $name] = true; + } - $skippedProperties[$prefix . $name] = true; + $filter = ReflectionProperty::IS_PRIVATE; + $reflector = $reflector->getParentClass(); } uksort($skippedProperties, 'strnatcmp'); diff --git a/tests/Doctrine/Tests/Models/GH10336/GH10336Entity.php b/tests/Doctrine/Tests/Models/GH10336/GH10336Entity.php new file mode 100644 index 00000000000..9c4fe9da08b --- /dev/null +++ b/tests/Doctrine/Tests/Models/GH10336/GH10336Entity.php @@ -0,0 +1,27 @@ +createSchemaForModels( + GH10336Entity::class, + GH10336Relation::class + ); + } + + public function testCanAccessRelationPropertyAfterClear(): void + { + $relation = new GH10336Relation(); + $relation->value = 'foo'; + $entity = new GH10336Entity(); + $entity->relation = $relation; + + $this->_em->persist($entity); + $this->_em->persist($relation); + $this->_em->flush(); + $this->_em->clear(); + + $entity = $this->_em->find(GH10336Entity::class, 1); + + $this->_em->clear(); + + $this->assertSame('foo', $entity->relation->value); + } +}