Sfoglia il codice sorgente

[all] added object wrappers in order to avoid issues with uninitialized proxies or detached objects

gediminasm 14 anni fa
parent
commit
d8d587851c

+ 19 - 0
lib/Gedmo/Exception/UnsupportedObjectManagerException.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Gedmo\Exception;
+
+use Gedmo\Exception;
+
+/**
+ * UnsupportedObjectManager
+ *
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
+ * @package Gedmo.Exception
+ * @subpackage UnsupportedObjectManager
+ * @link http://www.gediminasm.org
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+class UnsupportedObjectManager
+    extends InvalidArgumentException
+    implements Exception
+{}

+ 8 - 8
lib/Gedmo/Loggable/Document/Repository/LogEntryRepository.php

@@ -2,6 +2,7 @@
 
 namespace Gedmo\Loggable\Document\Repository;
 
+use Gedmo\Tool\Wrapper\MongoDocumentWrapper;
 use Gedmo\Loggable\LoggableListener;
 use Doctrine\ODM\MongoDB\DocumentRepository,
     Doctrine\ODM\MongoDB\Cursor;
@@ -34,13 +35,12 @@ class LogEntryRepository extends DocumentRepository
      */
     public function getLogEntries($document)
     {
-        $objectClass = get_class($document);
-        $objectMeta = $this->dm->getClassMetadata($objectClass);
-        $objectId = $objectMeta->getReflectionProperty($objectMeta->identifier)->getValue($document);
+        $wrapped = new MongoDocumentWrapper($document, $this->dm);
+        $objectId = $wrapped->getIdentifier();
 
         $qb = $this->createQueryBuilder();
         $qb->field('objectId')->equals($objectId);
-        $qb->field('objectClass')->equals($objectMeta->name);
+        $qb->field('objectClass')->equals($wrapped->getMetadata()->name);
         $qb->sort('version', 'DESC');
         $q = $qb->getQuery();
 
@@ -64,10 +64,10 @@ class LogEntryRepository extends DocumentRepository
      */
     public function revert($document, $version = 1)
     {
-        $objectClass = get_class($document);
-        $objectMeta = $this->dm->getClassMetadata($objectClass);
+        $wrapped = new MongoDocumentWrapper($document, $this->dm);
+        $objectMeta = $wrapped->getMetadata();
         $meta = $this->getClassMetadata();
-        $objectId = $objectMeta->getReflectionProperty($objectMeta->identifier)->getValue($document);
+        $objectId = $wrapped->getIdentifier();
 
         $qb = $this->createQueryBuilder();
         $qb->field('objectId')->equals($objectId);
@@ -92,7 +92,7 @@ class LogEntryRepository extends DocumentRepository
                                 $mapping = $objectMeta->getFieldMapping($field);
                                 $value = $value ? $this->dm->getReference($mapping['targetDocument'], current($value)) : null;
                             }
-                            $objectMeta->getReflectionProperty($field)->setValue($document, $value);
+                            $wrapped->setPropertyValue($field, $value);
                             unset($fields[array_search($field, $fields)]);
                         }
                     }

+ 10 - 9
lib/Gedmo/Loggable/Entity/Repository/LogEntryRepository.php

@@ -2,6 +2,7 @@
 
 namespace Gedmo\Loggable\Entity\Repository;
 
+use Gedmo\Tool\Wrapper\EntityWrapper;
 use Doctrine\ORM\EntityRepository;
 use Gedmo\Loggable\LoggableListener;
 
@@ -45,16 +46,15 @@ class LogEntryRepository extends EntityRepository
      */
     public function getLogEntriesQuery($entity)
     {
-        $objectClass = get_class($entity);
-        $objectMeta = $this->_em->getClassMetadata($objectClass);
+        $wrapped = new EntityWrapper($entity, $this->_em);
+        $objectClass = $wrapped->getMetadata()->name;
         $meta = $this->getClassMetadata();
         $dql = "SELECT log FROM {$meta->name} log";
         $dql .= " WHERE log.objectId = :objectId";
         $dql .= " AND log.objectClass = :objectClass";
         $dql .= " ORDER BY log.version DESC";
 
-        $identifierField = $objectMeta->getSingleIdentifierFieldName();
-        $objectId = $objectMeta->getReflectionProperty($identifierField)->getValue($entity);
+        $objectId = $wrapped->getIdentifier();
         $q = $this->_em->createQuery($dql);
         $q->setParameters(compact('objectId', 'objectClass', 'order'));
         return $q;
@@ -73,8 +73,10 @@ class LogEntryRepository extends EntityRepository
      */
     public function revert($entity, $version = 1)
     {
-        $objectClass = get_class($entity);
-        $objectMeta = $this->_em->getClassMetadata($objectClass);
+        $wrapped = new EntityWrapper($entity, $this->_em);
+        $objectMeta = $wrapped->getMetadata();
+        $objectClass = $objectMeta->name;
+        //$objectMeta = $this->_em->getClassMetadata($objectClass);
         $meta = $this->getClassMetadata();
         $dql = "SELECT log FROM {$meta->name} log";
         $dql .= " WHERE log.objectId = :objectId";
@@ -82,8 +84,7 @@ class LogEntryRepository extends EntityRepository
         $dql .= " AND log.version <= :version";
         $dql .= " ORDER BY log.version ASC";
 
-        $identifierField = $objectMeta->getSingleIdentifierFieldName();
-        $objectId = $objectMeta->getReflectionProperty($identifierField)->getValue($entity);
+        $objectId = $wrapped->getIdentifier();
         $q = $this->_em->createQuery($dql);
         $q->setParameters(compact('objectId', 'objectClass', 'version'));
         $logs = $q->getResult();
@@ -100,7 +101,7 @@ class LogEntryRepository extends EntityRepository
                                 $mapping = $objectMeta->getAssociationMapping($field);
                                 $value = $value ? $this->_em->getReference($mapping['targetEntity'], $value) : null;
                             }
-                            $objectMeta->getReflectionProperty($field)->setValue($entity, $value);
+                            $wrapped->setPropertyValue($field, $value);
                             unset($fields[array_search($field, $fields)]);
                         }
                     }

+ 99 - 0
lib/Gedmo/Tool/Wrapper/AbstractWrapper.php

@@ -0,0 +1,99 @@
+<?php
+
+namespace Gedmo\Tool\Wrapper;
+
+use Doctrine\ORM\EntityManager;
+use Doctrine\ODM\MongoDB\DocumentManager;
+use Doctrine\Common\Persistence\ObjectManager;
+use Gedmo\Tool\WrapperInterface;
+use Gedmo\Exception\UnsupportedObjectManager;
+
+/**
+ * Wraps entity or proxy for more convenient
+ * manipulation
+ *
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
+ * @package Gedmo.Tool.Wrapper
+ * @subpackage EntityWrapper
+ * @link http://www.gediminasm.org
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+abstract class AbstractWrapper implements WrapperInterface
+{
+    /**
+     * Object metadata
+     *
+     * @var \Doctrine\Common\Persistence\Mapping\ClassMetadata
+     */
+    protected $meta;
+
+    /**
+     * Wrapped object
+     *
+     * @var object
+     */
+    protected $object;
+
+    /**
+     * Object manager instance
+     *
+     * @var \Doctrine\Common\Persistence\ObjectManager
+     */
+    protected $om;
+
+    /**
+     * List of wrapped object references
+     *
+     * @var array
+     */
+    private static $wrappedObjectReferences;
+
+    /**
+     * Wrapp object factory method
+     *
+     * @param object $object
+     * @param \Doctrine\Common\Persistence\ObjectManager $om
+     * @return \Gedmo\Tool\WrapperInterface
+     */
+    public static function wrapp($object, ObjectManager $om)
+    {
+        $oid = spl_object_hash($object);
+        if (!isset(self::$wrappedObjectReferences[$oid])) {
+            if ($om instanceof EntityManager) {
+                self::$wrappedObjectReferences[$oid] = new EntityWrapper($object, $om);
+            } elseif ($om instanceof DocumentManager) {
+                self::$wrappedObjectReferences[$oid] = new MongoDocumentWrapper($object, $om);
+            } else {
+                throw new UnsupportedObjectManager('Given object manager is not managed by wrapper');
+            }
+        }
+        return self::$wrappedObjectReferences[$oid];
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getObject()
+    {
+        return $this->object;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMetadata()
+    {
+        return $this->meta;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function populate(array $data)
+    {
+        foreach ($data as $field => $value) {
+            $this->setPropertyValue($field, $value);
+        }
+        return $this;
+    }
+}

+ 30 - 87
lib/Gedmo/Tool/Wrapper/EntityWrapper.php

@@ -15,35 +15,14 @@ use Doctrine\ORM\Proxy\Proxy;
  * @link http://www.gediminasm.org
  * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-class EntityWrapper
+class EntityWrapper extends AbstractWrapper
 {
-    /**
-     * Wrapped Entity
-     *
-     * @var object
-     */
-    protected $entity;
-
-    /**
-     * EntityManager instance
-     *
-     * @var \Doctrine\ORM\EntityManager
-     */
-    protected $em;
-
-    /**
-     * Entity metadata
-     *
-     * @var \Doctrine\ORM\Mapping\ClassMetadata
-     */
-    protected $meta;
-
     /**
      * Entity identifier
      *
      * @var array
      */
-    private $identifier = false;
+    private $identifier;
 
     /**
      * True if entity or proxy is loaded
@@ -60,103 +39,67 @@ class EntityWrapper
      */
     public function __construct($entity, EntityManager $em)
     {
-        $this->em = $em;
-        $this->entity = $entity;
-        $this->meta = $em->getClassMetadata(get_class($this->entity));
+        $this->om = $em;
+        $this->object = $entity;
+        $this->meta = $em->getClassMetadata(get_class($this->object));
     }
 
     /**
-     * Get property value
-     *
-     * @param string $property
-     * @return mixed
+     * {@inheritDoc}
      */
     public function getPropertyValue($property)
     {
         $this->initialize();
-        return $this->meta->getReflectionProperty($property)->getValue($this->entity);
+        return $this->meta->getReflectionProperty($property)->getValue($this->object);
     }
 
     /**
-     * Populates the entity with given property values
-     *
-     * @param array $data
-     * @return \Gedmo\Tool\Wrapper\EntityWrapper
-     */
-    public function populate(array $data)
-    {
-        foreach ($data as $field => $value) {
-            $this->setPropertyValue($field, $value);
-        }
-        return $this;
-    }
-
-    /**
-     * Set the property
-     *
-     * @param string $property
-     * @param mixed $value
-     * @return \Gedmo\Tool\Wrapper\EntityWrapper
+     * {@inheritDoc}
      */
     public function setPropertyValue($property, $value)
     {
         $this->initialize();
-        $this->meta->getReflectionProperty($property)->setValue($this->entity, $value);
+        $this->meta->getReflectionProperty($property)->setValue($this->object, $value);
         return $this;
     }
 
     /**
-     * Checks if identifier is valid
-     *
-     * @return boolean
+     * {@inheritDoc}
      */
     public function hasValidIdentifier()
     {
-        $result = true;
-        foreach ($this->getIdentifier(false) as $field => $id) {
-            if (!$id) {
-                $result = false;
-                break;
-            }
-        }
-        return $result;
-    }
-
-    /**
-     * Get entity class name
-     *
-     * @return string
-     */
-    public function getClassName()
-    {
-        return $this->meta->name;
+        return (bool)$this->getIdentifier();
     }
 
     /**
-     * Get the entity identifier, single or composite
-     *
-     * @param boolean $single
-     * @return array|mixed
+     * {@inheritDoc}
      */
     public function getIdentifier($single = true)
     {
-        if (false === $this->identifier) {
-            if ($this->entity instanceof Proxy) {
-                $uow = $this->em->getUnitOfWork();
-                if ($uow->isInIdentityMap($this->entity)) {
-                    $this->identifier = $uow->getEntityIdentifier($this->entity);
+        if (!$this->identifier) {
+            if ($this->object instanceof Proxy) {
+                $uow = $this->om->getUnitOfWork();
+                if ($uow->isInIdentityMap($this->object)) {
+                    $this->identifier = $uow->getEntityIdentifier($this->object);
                 } else {
                     $this->initialize();
                 }
             }
-            if (false === $this->identifier) {
+            if (!$this->identifier) {
                 $this->identifier = array();
+                $incomplete = false;
                 foreach ($this->meta->identifier as $name) {
                     $this->identifier[$name] = $this->getPropertyValue($name);
+                    if (!$this->identifier[$name]) {
+                        $incomplete = true;
+                    }
+                }
+                if ($incomplete) {
+                    $this->identifier = null;
                 }
             }
         }
-        if ($single) {
+        if ($single && is_array($this->identifier)) {
             return reset($this->identifier);
         }
         return $this->identifier;
@@ -169,10 +112,10 @@ class EntityWrapper
     protected function initialize()
     {
         if (!$this->initialized) {
-            if ($this->entity instanceof Proxy) {
-                $uow = $this->em->getUnitOfWork();
-                if (!$this->entity->__isInitialized__) {
-                    $this->entity->__load();
+            if ($this->object instanceof Proxy) {
+                $uow = $this->om->getUnitOfWork();
+                if (!$this->object->__isInitialized__) {
+                    $this->object->__load();
                 }
             }
         }

+ 25 - 82
lib/Gedmo/Tool/Wrapper/MongoDocumentWrapper.php

@@ -15,35 +15,14 @@ use Doctrine\ODM\MongoDB\Proxy\Proxy;
  * @link http://www.gediminasm.org
  * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-class MongoDocumentWrapper
+class MongoDocumentWrapper extends AbstractWrapper
 {
-    /**
-     * Wrapped Document
-     *
-     * @var object
-     */
-    protected $document;
-
-    /**
-     * DocumentManager instance
-     *
-     * @var \Doctrine\ODM\MongoDB\DocumentManager
-     */
-    protected $dm;
-
-    /**
-     * Document metadata
-     *
-     * @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadata
-     */
-    protected $meta;
-
     /**
      * Document identifier
      *
      * @var mixed
      */
-    private $identifier = false;
+    private $identifier;
 
     /**
      * True if document or proxy is loaded
@@ -60,55 +39,32 @@ class MongoDocumentWrapper
      */
     public function __construct($document, DocumentManager $dm)
     {
-        $this->dm = $dm;
-        $this->document = $document;
-        $this->meta = $dm->getClassMetadata(get_class($this->document));
+        $this->om = $dm;
+        $this->object = $document;
+        $this->meta = $dm->getClassMetadata(get_class($this->object));
     }
 
     /**
-     * Get property value
-     *
-     * @param string $property
-     * @return mixed
+     * {@inheritDoc}
      */
     public function getPropertyValue($property)
     {
         $this->initialize();
-        return $this->meta->getReflectionProperty($property)->getValue($this->document);
-    }
-
-    /**
-     * Populates the document with given property values
-     *
-     * @param array $data
-     * @return \Gedmo\Tool\Wrapper\MongoDocumentWrapper
-     */
-    public function populate(array $data)
-    {
-        foreach ($data as $field => $value) {
-            $this->setPropertyValue($field, $value);
-        }
-        return $this;
+        return $this->meta->getReflectionProperty($property)->getValue($this->object);
     }
 
     /**
-     * Set the property
-     *
-     * @param string $property
-     * @param mixed $value
-     * @return \Gedmo\Tool\Wrapper\MongoDocumentWrapper
+     * {@inheritDoc}
      */
     public function setPropertyValue($property, $value)
     {
         $this->initialize();
-        $this->meta->getReflectionProperty($property)->setValue($this->document, $value);
+        $this->meta->getReflectionProperty($property)->setValue($this->object, $value);
         return $this;
     }
 
     /**
-     * Checks if identifier is valid
-     *
-     * @return boolean
+     * {@inheritDoc}
      */
     public function hasValidIdentifier()
     {
@@ -116,33 +72,20 @@ class MongoDocumentWrapper
     }
 
     /**
-     * Get document class name
-     *
-     * @return string
-     */
-    public function getClassName()
-    {
-        return $this->meta->name;
-    }
-
-    /**
-     * Get the document identifier, single or composite
-     *
-     * @param boolean $single
-     * @return array|mixed
+     * {@inheritDoc}
      */
     public function getIdentifier($single = true)
     {
-        if (false === $this->identifier) {
-            if ($this->document instanceof Proxy) {
-                $uow = $this->dm->getUnitOfWork();
-                if ($uow->isInIdentityMap($this->document)) {
-                    $this->identifier = (string)$uow->getDocumentIdentifier($this->document);
+        if (!$this->identifier) {
+            if ($this->object instanceof Proxy) {
+                $uow = $this->om->getUnitOfWork();
+                if ($uow->isInIdentityMap($this->object)) {
+                    $this->identifier = (string)$uow->getDocumentIdentifier($this->object);
                 } else {
                     $this->initialize();
                 }
             }
-            if (false === $this->identifier) {
+            if (!$this->identifier) {
                 $this->identifier = (string)$this->getPropertyValue($this->meta->identifier);
             }
         }
@@ -156,21 +99,21 @@ class MongoDocumentWrapper
     protected function initialize()
     {
         if (!$this->initialized) {
-            if ($this->document instanceof Proxy) {
-                $uow = $this->dm->getUnitOfWork();
-                if (!$this->document->__isInitialized__) {
+            if ($this->object instanceof Proxy) {
+                $uow = $this->om->getUnitOfWork();
+                if (!$this->object->__isInitialized__) {
                     $persister = $uow->getDocumentPersister($this->meta->name);
                     $identifier = null;
-                    if ($uow->isInIdentityMap($this->document)) {
+                    if ($uow->isInIdentityMap($this->object)) {
                         $identifier = $this->getIdentifier();
                     } else {
                         // this may not happen but in case
-                        $reflProperty = new \ReflectionProperty($this->document, 'identifier');
+                        $reflProperty = new \ReflectionProperty($this->object, 'identifier');
                         $reflProperty->setAccessible(true);
-                        $identifier = $reflProperty->getValue($this->document);
+                        $identifier = $reflProperty->getValue($this->object);
                     }
-                    $this->document->__isInitialized__ = true;
-                    $persister->load($identifier, $this->document);
+                    $this->object->__isInitialized__ = true;
+                    $persister->load($identifier, $this->object);
                 }
             }
         }

+ 70 - 0
lib/Gedmo/Tool/WrapperInterface.php

@@ -0,0 +1,70 @@
+<?php
+
+namespace Gedmo\Tool;
+
+/**
+ * Object wrapper interface
+ *
+ * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
+ * @package Gedmo.Tool
+ * @subpackage WrapperInterface
+ * @link http://www.gediminasm.org
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+interface WrapperInterface
+{
+    /**
+     * Get currently wrapped object
+     * etc.: entity, document
+     *
+     * @return object
+     */
+    function getObject();
+
+    /**
+     * Extract property value from object
+     *
+     * @param string $property
+     * @return mixed
+     */
+    function getPropertyValue($property);
+
+    /**
+     * Set the property
+     *
+     * @param string $property
+     * @param mixed $value
+     * @return \Gedmo\Tool\WrapperInterface
+     */
+    function setPropertyValue($property, $value);
+
+    /**
+     * Populates the object with given property values
+     *
+     * @param array $data
+     * @return \Gedmo\Tool\WrapperInterface
+     */
+    function populate(array $data);
+
+    /**
+     * Checks if identifier is valid
+     *
+     * @return boolean
+     */
+    function hasValidIdentifier();
+
+    /**
+     * Get metadata
+     *
+     * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata
+     */
+    function getMetadata();
+
+    /**
+     * Get the object identifier, $single or composite
+     *
+     * @param boolean $single
+     * @return array|mixed
+     */
+    function getIdentifier($single = true);
+}

+ 1 - 1
lib/Gedmo/Translatable/Document/Repository/TranslationRepository.php

@@ -66,7 +66,7 @@ class TranslationRepository extends DocumentRepository
             $translationMeta = $this->getClassMetadata();
             $qb = $this->createQueryBuilder();
             $q = $qb->field('foreignKey')->equals($documentId)
-                ->field('objectClass')->equals($wrapped->getClassName())
+                ->field('objectClass')->equals($wrapped->getMetadata()->name)
                 ->sort('locale', 'asc')
                 ->getQuery();
 

+ 1 - 1
lib/Gedmo/Translatable/Entity/Repository/TranslationRepository.php

@@ -61,7 +61,7 @@ class TranslationRepository extends EntityRepository
         $wrapped = new EntityWrapper($entity, $this->_em);
         if ($wrapped->hasValidIdentifier()) {
             $entityId = $wrapped->getIdentifier();
-            $entityClass = $wrapped->getClassName();
+            $entityClass = $wrapped->getMetadata()->name;
 
             $translationMeta = $this->getClassMetadata(); // table inheritance support
             $qb = $this->_em->createQueryBuilder();

+ 10 - 7
lib/Gedmo/Translatable/Mapping/Event/Adapter/ODM.php

@@ -3,6 +3,7 @@
 namespace Gedmo\Translatable\Mapping\Event\Adapter;
 
 use Gedmo\Mapping\Event\Adapter\ODM as BaseAdapterODM;
+use Gedmo\Tool\Wrapper\AbstractWrapper;
 use Doctrine\ODM\MongoDB\DocumentManager;
 use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
 use Doctrine\ODM\MongoDB\Cursor;
@@ -35,15 +36,15 @@ final class ODM extends BaseAdapterODM implements TranslatableAdapter
     public function loadTranslations($object, $translationClass, $locale)
     {
         $dm = $this->getObjectManager();
-        $meta = $dm->getClassMetadata(get_class($object));
+        $wrapped = AbstractWrapper::wrapp($object, $dm);
 
         // load translated content for all translatable fields
-        $identifier = $this->extractIdentifier($dm, $object);
+        $identifier = $wrapped->getIdentifier();
         // construct query
         $qb = $dm->createQueryBuilder($translationClass);
         $q = $qb->field('foreignKey')->equals($identifier)
             ->field('locale')->equals($locale)
-            ->field('objectClass')->equals($meta->name)
+            ->field('objectClass')->equals($wrapped->getMetadata()->name)
             ->getQuery();
 
         $q->setHydrate(false);
@@ -115,11 +116,12 @@ final class ODM extends BaseAdapterODM implements TranslatableAdapter
     public function getTranslationValue($object, $field, $value = false)
     {
         $dm = $this->getObjectManager();
-        $meta = $dm->getClassMetadata(get_class($object));
+        $wrapped = AbstractWrapper::wrapp($object, $dm);
+        $meta = $wrapped->getMetadata();
         $mapping = $meta->getFieldMapping($field);
         $type = Type::getType($mapping['type']);
         if ($value === false) {
-            $value = $meta->getReflectionProperty($field)->getValue($object);
+            $value = $wrapped->getPropertyValue($field);
         }
         return $type->convertToDatabaseValue($value);
     }
@@ -130,11 +132,12 @@ final class ODM extends BaseAdapterODM implements TranslatableAdapter
     public function setTranslationValue($object, $field, $value)
     {
         $dm = $this->getObjectManager();
-        $meta = $dm->getClassMetadata(get_class($object));
+        $wrapped = AbstractWrapper::wrapp($object, $dm);
+        $meta = $wrapped->getMetadata();
         $mapping = $meta->getFieldMapping($field);
         $type = Type::getType($mapping['type']);
 
         $value = $type->convertToPHPValue($value);
-        $meta->getReflectionProperty($field)->setValue($object, $value);
+        $wrapped->setPropertyValue($field, $value);
     }
 }

+ 10 - 7
lib/Gedmo/Translatable/Mapping/Event/Adapter/ORM.php

@@ -7,6 +7,7 @@ use Doctrine\ORM\EntityManager;
 use Doctrine\ORM\Mapping\ClassMetadataInfo;
 use Doctrine\ORM\Query;
 use Gedmo\Translatable\Mapping\Event\TranslatableAdapter;
+use Gedmo\Tool\Wrapper\AbstractWrapper;
 use Doctrine\DBAL\Types\Type;
 
 /**
@@ -35,16 +36,16 @@ final class ORM extends BaseAdapterORM implements TranslatableAdapter
     public function loadTranslations($object, $translationClass, $locale)
     {
         $em = $this->getObjectManager();
-        $meta = $em->getClassMetadata(get_class($object));
+        $wrapped = AbstractWrapper::wrapp($object, $em);
         // load translated content for all translatable fields
-        $objectId = $this->extractIdentifier($em, $object);
+        $objectId = $wrapped->getIdentifier();
         // construct query
         $dql = 'SELECT t.content, t.field FROM ' . $translationClass . ' t';
         $dql .= ' WHERE t.foreignKey = :objectId';
         $dql .= ' AND t.locale = :locale';
         $dql .= ' AND t.objectClass = :objectClass';
         // fetch results
-        $objectClass = $meta->name;
+        $objectClass = $wrapped->getMetadata()->name;
         $q = $em->createQuery($dql);
         $q->setParameters(compact('objectId', 'locale', 'objectClass'));
         return $q->getArrayResult();
@@ -118,10 +119,11 @@ final class ORM extends BaseAdapterORM implements TranslatableAdapter
     public function getTranslationValue($object, $field, $value = false)
     {
         $em = $this->getObjectManager();
-        $meta = $em->getClassMetadata(get_class($object));
+        $wrapped = AbstractWrapper::wrapp($object, $em);
+        $meta = $wrapped->getMetadata();
         $type = Type::getType($meta->getTypeOfField($field));
         if ($value === false) {
-            $value = $meta->getReflectionProperty($field)->getValue($object);
+            $value = $wrapped->getPropertyValue($field);
         }
         return $type->convertToDatabaseValue($value, $em->getConnection()->getDatabasePlatform());
     }
@@ -132,9 +134,10 @@ final class ORM extends BaseAdapterORM implements TranslatableAdapter
     public function setTranslationValue($object, $field, $value)
     {
         $em = $this->getObjectManager();
-        $meta = $em->getClassMetadata(get_class($object));
+        $wrapped = AbstractWrapper::wrapp($object, $em);
+        $meta = $wrapped->getMetadata();
         $type = Type::getType($meta->getTypeOfField($field));
         $value = $type->convertToPHPValue($value, $em->getConnection()->getDatabasePlatform());
-        $meta->getReflectionProperty($field)->setValue($object, $value);
+        $wrapped->setPropertyValue($field, $value);
     }
 }

+ 15 - 11
lib/Gedmo/Translatable/TranslationListener.php

@@ -2,6 +2,7 @@
 
 namespace Gedmo\Translatable;
 
+use Gedmo\Tool\Wrapper\AbstractWrapper;
 use Doctrine\Common\EventArgs,
     Doctrine\Common\Persistence\Mapping\ClassMetadata,
     Gedmo\Mapping\MappedEventSubscriber,
@@ -287,9 +288,9 @@ class TranslationListener extends MappedEventSubscriber
             $meta = $om->getClassMetadata(get_class($object));
             $config = $this->getConfiguration($om, $meta->name);
             if (isset($config['fields'])) {
-                $objectId = $ea->extractIdentifier($om, $object);
+                $wrapped = AbstractWrapper::wrapp($object, $om);
                 $transClass = $this->getTranslationClass($ea, $meta->name);
-                $ea->removeAssociatedTranslations($objectId, $transClass);
+                $ea->removeAssociatedTranslations($wrapped->getIdentifier(), $transClass);
             }
         }
     }
@@ -312,7 +313,8 @@ class TranslationListener extends MappedEventSubscriber
             $oid = spl_object_hash($object);
             if (array_key_exists($oid, $this->pendingTranslationInserts)) {
                 // load the pending translations without key
-                $objectId = $ea->extractIdentifier($om, $object);
+                $wrapped = AbstractWrapper::wrapp($object, $om);
+                $objectId = $wrapped->getIdentifier();
                 foreach ($this->pendingTranslationInserts[$oid] as $translation) {
                     $translation->setForeignKey($objectId);
                     $ea->insertTranslationRecord($translation);
@@ -412,13 +414,14 @@ class TranslationListener extends MappedEventSubscriber
     private function handleTranslatableObjectUpdate(TranslatableAdapter $ea, $object, $isInsert)
     {
         $om = $ea->getObjectManager();
-        $meta = $om->getClassMetadata(get_class($object));
+        $wrapped = AbstractWrapper::wrapp($object, $om);
+        $meta = $wrapped->getMetadata();
         // no need cache, metadata is loaded only once in MetadataFactoryClass
         $translationClass = $this->getTranslationClass($ea, $meta->name);
         $translationMetadata = $om->getClassMetadata($translationClass);
 
         // check for the availability of the primary key
-        $objectId = $ea->extractIdentifier($om, $object);
+        $objectId = $wrapped->getIdentifier();
         // load the currently used locale
         $locale = $this->getTranslatableLocale($object, $meta);
 
@@ -456,9 +459,9 @@ class TranslationListener extends MappedEventSubscriber
             }
 
             // set the translated field, take value using reflection
-            $value = $meta->getReflectionProperty($field)->getValue($object);
+            $value = $wrapped->getPropertyValue($field);
             $translation->setContent($ea->getTranslationValue($object, $field));
-            if ($isInsert && is_null($objectId)) {
+            if ($isInsert && !$objectId) {
                 // if we do not have the primary key yet available
                 // keep this translation in memory to insert it later with foreign key
                 $this->pendingTranslationInserts[spl_object_hash($object)][] = $translation;
@@ -476,7 +479,7 @@ class TranslationListener extends MappedEventSubscriber
             foreach ($changeSet as $field => $changes) {
                 if (in_array($field, $translatableFields)) {
                     if ($locale != $this->defaultLocale && strlen($changes[0])) {
-                        $meta->getReflectionProperty($field)->setValue($object, $changes[0]);
+                        $wrapped->setPropertyValue($field, $changes[0]);
                         $ea->setOriginalObjectProperty($uow, $oid, $field, $changes[0]);
                         unset($modifiedChangeSet[$field]);
                     }
@@ -509,8 +512,9 @@ class TranslationListener extends MappedEventSubscriber
         if (isset($this->additionalTranslations[$oid])) {
             $om = $ea->getObjectManager();
             $uow = $om->getUnitOfWork();
-            $meta = $om->getClassMetadata(get_class($object));
-            $objectId = $ea->extractIdentifier($om, $object);
+            $wrapped = AbstractWrapper::wrapp($object, $om);
+            $meta = $wrapped->getMetadata();
+            $objectId = $wrapped->getIdentifier();
             $transClass = $this->getTranslationClass($ea, $meta->name);
             foreach ($this->additionalTranslations[$oid] as $field => $translations) {
                 foreach ($translations as $locale => $content) {
@@ -527,7 +531,7 @@ class TranslationListener extends MappedEventSubscriber
                     }
                     $trans->setContent($ea->getTranslationValue($object, $field, $content));
                     if ($inserting && !$objectId) {
-                        $this->pendingTranslationInserts[spl_object_hash($object)][] = $trans;
+                        $this->pendingTranslationInserts[$oid][] = $trans;
                     } elseif ($trans->getId()) {
                         $om->persist($trans);
                         $transMeta = $om->getClassMetadata($transClass);

+ 5 - 3
lib/Gedmo/Tree/Entity/Repository/ClosureTreeRepository.php

@@ -6,6 +6,7 @@ use Gedmo\Exception\InvalidArgumentException;
 use Doctrine\ORM\Query;
 use Gedmo\Tree\Strategy;
 use Gedmo\Tree\Strategy\ORM\Closure;
+use Gedmo\Tool\Wrapper\EntityWrapper;
 use Doctrine\ORM\Proxy\Proxy;
 
 /**
@@ -225,13 +226,14 @@ class ClosureTreeRepository extends AbstractTreeRepository
         if (!$node instanceof $meta->name) {
             throw new InvalidArgumentException("Node is not related to this repository");
         }
-        if (!$this->_em->getUnitOfWork()->isInIdentityMap($node)) {
+        $wrapped = new EntityWrapper($node, $this->_em);
+        if (!$wrapped->hasValidIdentifier()) {
             throw new InvalidArgumentException("Node is not managed by UnitOfWork");
         }
         $config = $this->listener->getConfiguration($this->_em, $meta->name);
         $pk = $meta->getSingleIdentifierFieldName();
-        $nodeId = $meta->getReflectionProperty($pk)->getValue($node);
-        $parent = $meta->getReflectionProperty($config['parent'])->getValue($node);
+        $nodeId = $wrapped->getIdentifier();
+        $parent = $wrapped->getPropertyValue($config['parent']);
 
         $dql = "SELECT node FROM {$config['useObjectClass']} node";
         $dql .= " WHERE node.{$config['parent']} = :node";

+ 55 - 47
lib/Gedmo/Tree/Entity/Repository/NestedTreeRepository.php

@@ -2,6 +2,7 @@
 
 namespace Gedmo\Tree\Entity\Repository;
 
+use Gedmo\Tool\Wrapper\EntityWrapper;
 use Doctrine\ORM\Query,
     Gedmo\Tree\Strategy,
     Gedmo\Tree\Strategy\ORM\Nested,
@@ -74,6 +75,7 @@ class NestedTreeRepository extends AbstractTreeRepository
                 throw new \Gedmo\Exception\InvalidArgumentException('Node to persist must be available as first argument');
             }
             $node = $args[0];
+            $wrapped = new EntityWrapper($node, $this->_em);
             $meta = $this->getClassMetadata();
             $config = $this->listener->getConfiguration($this->_em, $meta->name);
             $position = substr($method, 9);
@@ -82,7 +84,7 @@ class NestedTreeRepository extends AbstractTreeRepository
                     throw new \Gedmo\Exception\InvalidArgumentException('If "Of" is specified you must provide parent or sibling as the second argument');
                 }
                 $parent = $args[1];
-                $meta->getReflectionProperty($config['parent'])->setValue($node, $parent);
+                $wrapped->setPropertyValue($config['parent'], $parent);
                 $position = substr($position, 0, -2);
             }
             $oid = spl_object_hash($node);
@@ -109,12 +111,13 @@ class NestedTreeRepository extends AbstractTreeRepository
         if (!$node instanceof $meta->name) {
             throw new InvalidArgumentException("Node is not related to this repository");
         }
-        if (!$this->_em->getUnitOfWork()->isInIdentityMap($node)) {
+        $config = $this->listener->getConfiguration($this->_em, $meta->name);
+        $wrapped = new EntityWrapper($node, $this->_em);
+        if (!$wrapped->hasValidIdentifier()) {
             throw new InvalidArgumentException("Node is not managed by UnitOfWork");
         }
-        $config = $this->listener->getConfiguration($this->_em, $meta->name);
-        $left = $meta->getReflectionProperty($config['left'])->getValue($node);
-        $right = $meta->getReflectionProperty($config['right'])->getValue($node);
+        $left = $wrapped->getPropertyValue($config['left']);
+        $right = $wrapped->getPropertyValue($config['right']);
         $qb = $this->_em->createQueryBuilder();
         $qb->select('node')
             ->from($config['useObjectClass'], 'node')
@@ -122,7 +125,7 @@ class NestedTreeRepository extends AbstractTreeRepository
             ->andWhere('node.' . $config['right'] . " >= :right")
             ->orderBy('node.' . $config['left'], 'ASC');
         if (isset($config['root'])) {
-            $rootId = $meta->getReflectionProperty($config['root'])->getValue($node);
+            $rootId = $wrapped->getPropertyValue($config['root']);
             $qb->andWhere("node.{$config['root']} = {$rootId}");
         }
         $q = $qb->getQuery();
@@ -157,25 +160,26 @@ class NestedTreeRepository extends AbstractTreeRepository
         $config = $this->listener->getConfiguration($this->_em, $meta->name);
         if (null !== $node) {
             if ($node instanceof $meta->name) {
-                if (!$this->_em->getUnitOfWork()->isInIdentityMap($node)) {
+                $wrapped = new EntityWrapper($node, $this->_em);
+                if (!$wrapped->hasValidIdentifier()) {
                     throw new InvalidArgumentException("Node is not managed by UnitOfWork");
                 }
                 if ($direct) {
-                    $id = $meta->getReflectionProperty($nodeId)->getValue($node);
+                    $id = $wrapped->getIdentifier();
                     $qb = $this->_em->createQueryBuilder();
                     $qb->select('COUNT(node.' . $nodeId . ')')
                         ->from($config['useObjectClass'], 'node')
                         ->where('node.' . $config['parent'] . ' = ' . $id);
 
                     if (isset($config['root'])) {
-                        $rootId = $meta->getReflectionProperty($config['root'])->getValue($node);
+                        $rootId = $wrapped->getPropertyValue($config['root']);
                         $qb->andWhere("node.{$config['root']} = {$rootId}");
                     }
                     $q = $qb->getQuery();
                     $count = intval($q->getSingleScalarResult());
                 } else {
-                    $left = $meta->getReflectionProperty($config['left'])->getValue($node);
-                    $right = $meta->getReflectionProperty($config['right'])->getValue($node);
+                    $left = $wrapped->getPropertyValue($config['left']);
+                    $right = $wrapped->getPropertyValue($config['right']);
                     if ($left && $right) {
                         $count = ($right - $left - 1) / 2;
                     }
@@ -214,23 +218,23 @@ class NestedTreeRepository extends AbstractTreeRepository
             ->from($config['useObjectClass'], 'node');
         if ($node !== null) {
             if ($node instanceof $meta->name) {
-                if (!$this->_em->getUnitOfWork()->isInIdentityMap($node)) {
+                $wrapped = new EntityWrapper($node, $this->_em);
+                if (!$wrapped->hasValidIdentifier()) {
                     throw new InvalidArgumentException("Node is not managed by UnitOfWork");
                 }
                 if ($direct) {
-                    $nodeId = $meta->getSingleIdentifierFieldName();
-                    $id = $meta->getReflectionProperty($nodeId)->getValue($node);
+                    $id = $wrapped->getIdentifier();
                     $qb->where('node.' . $config['parent'] . ' = ' . $id);
                 } else {
-                    $left = $meta->getReflectionProperty($config['left'])->getValue($node);
-                    $right = $meta->getReflectionProperty($config['right'])->getValue($node);
+                    $left = $wrapped->getPropertyValue($config['left']);
+                    $right = $wrapped->getPropertyValue($config['right']);
                     if ($left && $right) {
                         $qb->where('node.' . $config['right'] . " < {$right}")
                             ->andWhere('node.' . $config['left'] . " > {$left}");
                     }
                 }
                 if (isset($config['root'])) {
-                    $rootId = $meta->getReflectionProperty($config['root'])->getValue($node);
+                    $rootId = $wrapped->getPropertyValue($config['root']);
                     $qb->andWhere("node.{$config['root']} = {$rootId}");
                 }
             } else {
@@ -286,9 +290,6 @@ class NestedTreeRepository extends AbstractTreeRepository
             if (is_null($root)) {
                 throw new InvalidArgumentException("If tree has root, getLiefs method requires any node of this tree");
             }
-            if (!$this->_em->getUnitOfWork()->isInIdentityMap($root)) {
-                throw new InvalidArgumentException("Node is not managed by UnitOfWork");
-            }
         }
 
         $qb = $this->_em->createQueryBuilder();
@@ -297,7 +298,11 @@ class NestedTreeRepository extends AbstractTreeRepository
             ->where('node.' . $config['right'] . ' = 1 + node.' . $config['left']);
         if (isset($config['root'])) {
             if ($root instanceof $meta->name) {
-                $rootId = $meta->getReflectionProperty($config['root'])->getValue($root);
+                $wrapped = new EntityWrapper($root, $this->_em);
+                $rootId = $wrapped->getPropertyValue($config['root']);
+                if (!$rootId) {
+                    throw new InvalidArgumentException("Root node must be managed");
+                }
                 $qb->andWhere("node.{$config['root']} = {$rootId}");
             } else {
                 throw new InvalidArgumentException("Node is not related to this repository");
@@ -342,18 +347,20 @@ class NestedTreeRepository extends AbstractTreeRepository
         if (!$node instanceof $meta->name) {
             throw new InvalidArgumentException("Node is not related to this repository");
         }
-        if (!$this->_em->getUnitOfWork()->isInIdentityMap($node)) {
+        $wrapped = new EntityWrapper($node, $this->_em);
+        if (!$wrapped->hasValidIdentifier()) {
             throw new InvalidArgumentException("Node is not managed by UnitOfWork");
         }
 
         $config = $this->listener->getConfiguration($this->_em, $meta->name);
-        $parent = $meta->getReflectionProperty($config['parent'])->getValue($node);
+        $parent = $wrapped->getPropertyValue($config['parent']);
         if (!$parent) {
             throw new InvalidArgumentException("Cannot get siblings from tree root node");
         }
-        $parentId = current($this->_em->getUnitOfWork()->getEntityIdentifier($parent));
+        $wrappedParent = new EntityWrapper($parent, $this->_em);
+        $parentId = $wrappedParent->getIdentifier();
 
-        $left = $meta->getReflectionProperty($config['left'])->getValue($node);
+        $left = $wrapped->getPropertyValue($config['left']);
         $sign = $includeSelf ? '>=' : '>';
 
         $dql = "SELECT node FROM {$config['useObjectClass']} node";
@@ -389,18 +396,20 @@ class NestedTreeRepository extends AbstractTreeRepository
         if (!$node instanceof $meta->name) {
             throw new InvalidArgumentException("Node is not related to this repository");
         }
-        if (!$this->_em->getUnitOfWork()->isInIdentityMap($node)) {
+        $wrapped = new EntityWrapper($node, $this->_em);
+        if (!$wrapped->hasValidIdentifier()) {
             throw new InvalidArgumentException("Node is not managed by UnitOfWork");
         }
 
         $config = $this->listener->getConfiguration($this->_em, $meta->name);
-        $parent = $meta->getReflectionProperty($config['parent'])->getValue($node);
+        $parent = $wrapped->getPropertyValue($config['parent']);
         if (!$parent) {
             throw new InvalidArgumentException("Cannot get siblings from tree root node");
         }
-        $parentId = current($this->_em->getUnitOfWork()->getEntityIdentifier($parent));
+        $wrappedParent = new EntityWrapper($parent, $this->_em);
+        $parentId = $wrappedParent->getIdentifier();
 
-        $left = $meta->getReflectionProperty($config['left'])->getValue($node);
+        $left = $wrapped->getPropertyValue($config['left']);
         $sign = $includeSelf ? '<=' : '<';
 
         $dql = "SELECT node FROM {$config['useObjectClass']} node";
@@ -501,13 +510,14 @@ class NestedTreeRepository extends AbstractTreeRepository
     {
         $meta = $this->getClassMetadata();
         if ($node instanceof $meta->name) {
+            $wrapped = new EntityWrapper($node, $this->_em);
             $config = $this->listener->getConfiguration($this->_em, $meta->name);
-            $right = $meta->getReflectionProperty($config['right'])->getValue($node);
-            $left = $meta->getReflectionProperty($config['left'])->getValue($node);
-            $rootId = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($node) : null;
+            $right = $wrapped->getPropertyValue($config['right']);
+            $left = $wrapped->getPropertyValue($config['left']);
+            $rootId = isset($config['root']) ? $wrapped->getPropertyValue($config['root']) : null;
 
             if ($right == $left + 1) {
-                $this->removeSingle($node);
+                $this->removeSingle($wrapped);
                 $this->listener
                     ->getStrategy($this->_em, $meta->name)
                     ->shiftRL($this->_em, $config['useObjectClass'], $right, -2, $rootId);
@@ -516,13 +526,14 @@ class NestedTreeRepository extends AbstractTreeRepository
             // process updates in transaction
             $this->_em->getConnection()->beginTransaction();
             try {
-                $parent = $meta->getReflectionProperty($config['parent'])->getValue($node);
+                $parent = $wrapped->getPropertyValue($config['parent']);
                 $parentId = 'NULL';
                 if ($parent) {
-                    $parentId = current($this->_em->getUnitOfWork()->getEntityIdentifier($parent));
+                    $wrappedParrent = new EntityWrapper($parent, $this->_em);
+                    $parentId = $wrappedParrent->getIdentifier();
                 }
                 $pk = $meta->getSingleIdentifierFieldName();
-                $nodeId = $meta->getReflectionProperty($pk)->getValue($node);
+                $nodeId = $wrapped->getIdentifier();
                 $shift = -1;
 
                 // in case if root node is removed, childs become roots
@@ -581,7 +592,7 @@ class NestedTreeRepository extends AbstractTreeRepository
                         ->getStrategy($this->_em, $meta->name)
                         ->shiftRL($this->_em, $config['useObjectClass'], $right, -2, $rootId);
                 }
-                $this->removeSingle($node);
+                $this->removeSingle($wrapped);
                 $this->_em->getConnection()->commit();
             } catch (\Exception $e) {
                 $this->_em->close();
@@ -614,12 +625,9 @@ class NestedTreeRepository extends AbstractTreeRepository
 
             $nodes = $this->children($node, true, $sortByField, $direction);
             foreach ($nodes as $node) {
-                // this is overhead but had to be refreshed
-                if ($node instanceof Proxy && !$node->__isInitialized__) {
-                    $this->_em->refresh($node);
-                }
-                $right = $meta->getReflectionProperty($config['right'])->getValue($node);
-                $left = $meta->getReflectionProperty($config['left'])->getValue($node);
+                $wrapped = new EntityWrapper($node, $this->_em);
+                $right = $wrapped->getPropertyValue($config['right']);
+                $left = $wrapped->getPropertyValue($config['left']);
                 $this->moveDown($node, true);
                 if ($left != ($right - 1)) {
                     $this->reorder($node, $sortByField, $direction, false);
@@ -799,16 +807,16 @@ class NestedTreeRepository extends AbstractTreeRepository
      * Removes single node without touching children
      *
      * @internal
-     * @param object $node
+     * @param EntityWrapper $wrapped
      * @return void
      */
-    private function removeSingle($node)
+    private function removeSingle(EntityWrapper $wrapped)
     {
         $meta = $this->getClassMetadata();
         $config = $this->listener->getConfiguration($this->_em, $meta->name);
 
         $pk = $meta->getSingleIdentifierFieldName();
-        $nodeId = $meta->getReflectionProperty($pk)->getValue($node);
+        $nodeId = $wrapped->getIdentifier();
 
         // prevent from deleting whole branch
         $dql = "UPDATE {$config['useObjectClass']} node";
@@ -823,6 +831,6 @@ class NestedTreeRepository extends AbstractTreeRepository
         $this->_em->createQuery($dql)->getSingleScalarResult();
 
         // remove from identity map
-        $this->_em->getUnitOfWork()->removeFromIdentityMap($node);
+        $this->_em->getUnitOfWork()->removeFromIdentityMap($wrapped->getObject());
     }
 }

+ 31 - 32
lib/Gedmo/Tree/Strategy/ORM/Nested.php

@@ -2,7 +2,8 @@
 
 namespace Gedmo\Tree\Strategy\ORM;
 
-use Doctrine\ORM\Proxy\Proxy;
+use Gedmo\Tool\Wrapper\EntityWrapper;
+use Gedmo\Tool\Wrapper\AbstractWrapper;
 use Gedmo\Tree\Strategy,
     Doctrine\ORM\EntityManager,
     Gedmo\Tree\TreeListener,
@@ -160,13 +161,14 @@ class Nested implements Strategy
         $config = $this->listener->getConfiguration($em, $meta->name);
         $uow = $em->getUnitOfWork();
 
-        $leftValue = $meta->getReflectionProperty($config['left'])->getValue($node);
-        $rightValue = $meta->getReflectionProperty($config['right'])->getValue($node);
+        $wrapped = AbstractWrapper::wrapp($node, $em);
+        $leftValue = $wrapped->getPropertyValue($config['left']);
+        $rightValue = $wrapped->getPropertyValue($config['right']);
 
         if (!$leftValue || !$rightValue) {
             return;
         }
-        $rootId = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($node) : null;
+        $rootId = isset($config['root']) ? $wrapped->getPropertyValue($config['root']) : null;
         $diff = $rightValue - $leftValue + 1;
         if ($diff > 2) {
             $dql = "SELECT node FROM {$config['useObjectClass']} node";
@@ -228,15 +230,16 @@ class Nested implements Strategy
      */
     public function updateNode(EntityManager $em, $node, $parent, $position = 'FirstChild')
     {
-        $meta = $em->getClassMetadata(get_class($node));
+        $wrapped = AbstractWrapper::wrapp($node, $em);
+        $meta = $wrapped->getMetadata();
         $config = $this->listener->getConfiguration($em, $meta->name);
 
-        $rootId = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($node) : null;
+        $rootId = isset($config['root']) ? $wrapped->getPropertyValue($config['root']) : null;
         $identifierField = $meta->getSingleIdentifierFieldName();
-        $nodeId = $meta->getReflectionProperty($identifierField)->getValue($node);
+        $nodeId = $wrapped->getIdentifier();
 
-        $left = $meta->getReflectionProperty($config['left'])->getValue($node);
-        $right = $meta->getReflectionProperty($config['right'])->getValue($node);
+        $left = $wrapped->getPropertyValue($config['left']);
+        $right = $wrapped->getPropertyValue($config['right']);
 
         $isNewNode = empty($left) && empty($right);
         if ($isNewNode) {
@@ -252,33 +255,31 @@ class Nested implements Strategy
         $treeSize = $right - $left + 1;
         $newRootId = null;
         if ($parent) {
-            if ($parent instanceof Proxy && !$parent->__isInitialized__) {
-                $em->refresh($parent);
-            }
+            $wrappedParent = AbstractWrapper::wrapp($parent, $em);
 
-            $parentRootId = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($parent) : null;
-            $parentLeft = $meta->getReflectionProperty($config['left'])->getValue($parent);
-            $parentRight = $meta->getReflectionProperty($config['right'])->getValue($parent);
+            $parentRootId = isset($config['root']) ? $wrappedParent->getPropertyValue($config['root']) : null;
+            $parentLeft = $wrappedParent->getPropertyValue($config['left']);
+            $parentRight = $wrappedParent->getPropertyValue($config['right']);
             if (!$isNewNode && $rootId === $parentRootId && $parentLeft >= $left && $parentRight <= $right) {
                 throw new \Gedmo\Exception\UnexpectedValueException("Cannot set child as parent to node: {$nodeId}");
             }
             if (isset($config['level'])) {
-                $level = $meta->getReflectionProperty($config['level'])->getValue($parent);
+                $level = $wrappedParent->getPropertyValue($config['level']);
             }
             switch ($position) {
                 case self::PREV_SIBLING:
-                    $newParent = $meta->getReflectionProperty($config['parent'])->getValue($parent);
+                    $newParent = $wrappedParent->getPropertyValue($config['parent']);
                     if (!$isNewNode) {
-                        $meta->getReflectionProperty($config['parent'])->setValue($node, $newParent);
+                        $wrapped->setPropertyValue($config['parent'], $newParent);
                         $em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $node);
                     }
                     $start = $parentLeft;
                     break;
 
                 case self::NEXT_SIBLING:
-                    $newParent = $meta->getReflectionProperty($config['parent'])->getValue($parent);
+                    $newParent = $wrappedParent->getPropertyValue($config['parent']);
                     if (!$isNewNode) {
-                        $meta->getReflectionProperty($config['parent'])->setValue($node, $newParent);
+                        $wrapped->setPropertyValue($config['parent'], $newParent);
                         $em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $node);
                     }
                     $start = $parentRight + 1;
@@ -298,11 +299,11 @@ class Nested implements Strategy
             $this->shiftRL($em, $config['useObjectClass'], $start, $treeSize, $parentRootId);
             if (!$isNewNode && $rootId === $parentRootId && $left >= $start) {
                 $left += $treeSize;
-                $meta->getReflectionProperty($config['left'])->setValue($node, $left);
+                $wrapped->setPropertyValue($config['left'], $left);
             }
             if (!$isNewNode && $rootId === $parentRootId && $right >= $start) {
                 $right += $treeSize;
-                $meta->getReflectionProperty($config['right'])->setValue($node, $right);
+                $wrapped->setPropertyValue($config['right'], $right);
             }
             $newRootId = $parentRootId;
         } elseif (!isset($config['root'])) {
@@ -317,7 +318,7 @@ class Nested implements Strategy
 
         $diff = $start - $left;
         if (!$isNewNode) {
-            $levelDiff = isset($config['level']) ? $level - $meta->getReflectionProperty($config['level'])->getValue($node) : null;
+            $levelDiff = isset($config['level']) ? $level - $wrapped->getPropertyValue($config['level']) : null;
             $this->shiftRangeRL(
                 $em,
                 $config['useObjectClass'],
@@ -334,30 +335,28 @@ class Nested implements Strategy
             $qb->update($config['useObjectClass'], 'node');
             if (isset($config['root'])) {
                 $qb->set('node.' . $config['root'], $newRootId);
-                $meta->getReflectionProperty($config['root'])->setValue($node, $newRootId);
+                $wrapped->setPropertyValue($config['root'], $newRootId);
                 $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['root'], $newRootId);
             }
             if (isset($config['level'])) {
                 $qb->set('node.' . $config['level'], $level);
-                $meta->getReflectionProperty($config['level'])->setValue($node, $level);
+                $wrapped->setPropertyValue($config['level'], $level);
                 $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['level'], $level);
             }
             if (isset($newParent)) {
-                if ($newParent instanceof Proxy && !$newParent->__isInitialized__) {
-                    $em->refresh($newParent);
-                }
-                $newParentId = $meta->getReflectionProperty($identifierField)->getValue($newParent);
+                $wrappedNewParent = AbstractWrapper::wrapp($newParent, $em);
+                $newParentId = $wrappedNewParent->getIdentifier();
                 $qb->set('node.' . $config['parent'], $newParentId);
-                $meta->getReflectionProperty($config['parent'])->setValue($node, $newParent);
+                $wrapped->setPropertyValue($config['parent'], $newParent);
                 $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['parent'], $newParent);
             }
             $qb->set('node.' . $config['left'], $left + $diff);
             $qb->set('node.' . $config['right'], $right + $diff);
             $qb->where("node.{$identifierField} = {$nodeId}");
             $qb->getQuery()->getSingleScalarResult();
-            $meta->getReflectionProperty($config['left'])->setValue($node, $left + $diff);
+            $wrapped->setPropertyValue($config['left'], $left + $diff);
+            $wrapped->setPropertyValue($config['right'], $right + $diff);
             $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['left'], $left + $diff);
-            $meta->getReflectionProperty($config['right'])->setValue($node, $right + $diff);
             $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['right'], $right + $diff);
         }
     }