From a49ebfcd35af70351c66bc329c1e0501d4be4086 Mon Sep 17 00:00:00 2001 From: "Nathanael d. Noblet" Date: Wed, 29 Jul 2015 17:05:45 -0600 Subject: [PATCH 1/2] Allow DoctrineWriter to handle association values This allows some of an object graph to be updated. Really only useful once another PR related to using entity repository methods --- src/Writer/DoctrineWriter.php | 44 ++++++++++++++++ .../Fixtures/Entity/TestEntityAssociation.php | 37 ++++++++++++++ tests/Writer/DoctrineWriterTest.php | 50 +++++++++++++++++-- 3 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 tests/Fixtures/Entity/TestEntityAssociation.php diff --git a/src/Writer/DoctrineWriter.php b/src/Writer/DoctrineWriter.php index fca7cc38..d782fec9 100644 --- a/src/Writer/DoctrineWriter.php +++ b/src/Writer/DoctrineWriter.php @@ -176,6 +176,7 @@ public function writeItem(array $item) { $entity = $this->findOrCreateItem($item); + $this->updateAssociations($item,$entity); $this->loadAssociationObjectsToEntity($item, $entity); $this->updateEntity($item, $entity); @@ -210,6 +211,49 @@ protected function updateEntity(array $item, $entity) } } + /** + * @param array $item + * @param $entity + * @throws \Exception + */ + public function updateAssociations(array &$item, $entity) + { + foreach ($this->entityMetadata->getAssociationMappings() as $map) { + // There is data for the association + if (isset($item[$map['fieldName']]) && is_array($item[$map['fieldName']])) { + + $getterMethod = sprintf('get%s', $map['fieldName']); + + if (method_exists($entity, $getterMethod)) { + $orgRepository = $this->entityRepository; + $orgMetadata = $this->entityMetadata; + $orgName = $this->entityName; + + $this->entityRepository = $this->entityManager->getRepository($map['targetEntity']); + $this->entityMetadata = $this->entityManager->getClassMetadata($map['targetEntity']); + $this->entityName = $this->entityMetadata->getName(); + + $association = call_user_func([$entity,$getterMethod]); + if (!$association) { + $association = $this->getNewInstance(); + } + + $value = $item[$map['fieldName']]; + unset($item[$map['fieldName']]); + + $this->updateEntity($value, $association); + + $setterMethod = sprintf('set%s',$map['fieldName']); + call_user_func([$entity, $setterMethod],$association); + + $this->entityRepository = $orgRepository; + $this->entityMetadata = $orgMetadata; + $this->entityName = $orgName; + } + } + } + } + /** * Add the associated objects in case the item have for persist its relation * diff --git a/tests/Fixtures/Entity/TestEntityAssociation.php b/tests/Fixtures/Entity/TestEntityAssociation.php new file mode 100644 index 00000000..d1ca76ec --- /dev/null +++ b/tests/Fixtures/Entity/TestEntityAssociation.php @@ -0,0 +1,37 @@ +aProperty; + } + + public function setAProperty($firstProperty) + { + $this->aProperty = $firstProperty; + } + + public function getBProperty() + { + return $this->bProperty; + } + + public function setBProperty($secondProperty) + { + $this->bProperty = $secondProperty; + } +} \ No newline at end of file diff --git a/tests/Writer/DoctrineWriterTest.php b/tests/Writer/DoctrineWriterTest.php index 5792512e..70ae8c80 100644 --- a/tests/Writer/DoctrineWriterTest.php +++ b/tests/Writer/DoctrineWriterTest.php @@ -41,6 +41,8 @@ protected function getEntityManager() ->disableOriginalConstructor() ->getMock(); + $assocMetadata = clone $metadata; + $metadata->expects($this->any()) ->method('getName') ->will($this->returnValue('Ddeboer\DataImport\Tests\Fixtures\Entity\TestEntity')); @@ -55,7 +57,19 @@ protected function getEntityManager() $metadata->expects($this->any()) ->method('getAssociationMappings') - ->will($this->returnValue(array(array('fieldName' => 'firstAssociation','targetEntity' => 'Ddeboer\DataImport\Tests\Fixtures\Entity\TestEntity')))); + ->will($this->returnValue(array(array('fieldName' => 'firstAssociation','targetEntity' => 'Ddeboer\DataImport\Tests\Fixtures\Entity\TestEntityAssociation')))); + + $assocMetadata->expects($this->any()) + ->method('getName') + ->will($this->returnValue('Ddeboer\DataImport\Tests\Fixtures\Entity\TestEntityAssociation')); + + $assocMetadata->expects($this->any()) + ->method('getFieldNames') + ->will($this->returnValue(array('aProperty', 'bProperty'))); + + $assocMetadata->expects($this->any()) + ->method('getAssociationNames') + ->will($this->returnValue(array())); $configuration = $this->getMockBuilder('Doctrine\DBAL\Configuration') ->setMethods(array('getConnection')) @@ -83,13 +97,15 @@ protected function getEntityManager() ->method('executeQuery') ->with('TRUNCATE SQL'); - $em->expects($this->once()) + $em->expects($this->any()) ->method('getRepository') ->will($this->returnValue($repo)); - $em->expects($this->once()) + $em->expects($this->any()) ->method('getClassMetadata') - ->will($this->returnValue($metadata)); + ->will($this->returnCallback(function($arg) use ($metadata,$assocMetadata){ + return ($arg =='DdeboerDataImport:TestEntity') ? $metadata :$assocMetadata; + })); $em->expects($this->any()) ->method('getConnection') @@ -149,6 +165,32 @@ public function testLoadAssociationWithPresetObject() $writer->writeItem($item); } + public function testUpdateAssociationUnmapped() + { + $em = $this->getEntityManager(); + + $em->expects($this->never()) + ->method('persist'); + + $em->expects($this->never()) + ->method('getReference'); + + $association = new TestEntity(); + $writer = new DoctrineWriter($em, 'DdeboerDataImport:TestEntity'); + $item = [ + 'firstProperty' => 'some value', + 'secondProperty' => 'some other value', + 'firstAssociation' => [ + 'aProperty' => 'another value', + 'bProperty' => 'another other value', + ] + ]; + + $writer->updateAssociations($item,$association); + $this->assertInstanceOf('Ddeboer\DataImport\Tests\Fixtures\Entity\TestEntityAssociation',$association->getFirstAssociation()); + $this->assertEquals('another value',$association->getFirstAssociation()->getAProperty()); + } + /** * Test to make sure that we are clearing the write entity */ From 58614e8a78ebad6f3489d9e1442195cfc5af98e2 Mon Sep 17 00:00:00 2001 From: "Nathanael d. Noblet" Date: Mon, 3 Aug 2015 15:11:29 -0600 Subject: [PATCH 2/2] Split updateAssociations into two functions To allow for extensions and easier testing updateAssociations is split into two functions. One loops over associations and then calls the second updateAssociation function that does the metadata retrieval and the rest of the entity updating. --- src/Writer/DoctrineWriter.php | 59 +++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/Writer/DoctrineWriter.php b/src/Writer/DoctrineWriter.php index d782fec9..676cc99b 100644 --- a/src/Writer/DoctrineWriter.php +++ b/src/Writer/DoctrineWriter.php @@ -131,6 +131,7 @@ public function prepare() /** * Return a new instance of the entity * + * @throws \Exception * @return object */ protected function getNewInstance() @@ -221,36 +222,47 @@ public function updateAssociations(array &$item, $entity) foreach ($this->entityMetadata->getAssociationMappings() as $map) { // There is data for the association if (isset($item[$map['fieldName']]) && is_array($item[$map['fieldName']])) { + $this->updateAssociation($item, $entity, $map['fieldName'], $map['targetEntity']); + } + } + } - $getterMethod = sprintf('get%s', $map['fieldName']); + /** + * @param array $item + * @param object $entity + * @param string $fieldName + * @param string $targetEntity + * @throws \Exception + */ + public function updateAssociation(array &$item, $entity, $fieldName, $targetEntity) + { + $getterMethod = sprintf('get%s', $fieldName); - if (method_exists($entity, $getterMethod)) { - $orgRepository = $this->entityRepository; - $orgMetadata = $this->entityMetadata; - $orgName = $this->entityName; + if (method_exists($entity, $getterMethod)) { + $orgRepository = $this->entityRepository; + $orgMetadata = $this->entityMetadata; + $orgName = $this->entityName; - $this->entityRepository = $this->entityManager->getRepository($map['targetEntity']); - $this->entityMetadata = $this->entityManager->getClassMetadata($map['targetEntity']); - $this->entityName = $this->entityMetadata->getName(); + $this->entityRepository = $this->entityManager->getRepository($targetEntity); + $this->entityMetadata = $this->entityManager->getClassMetadata($targetEntity); + $this->entityName = $this->entityMetadata->getName(); - $association = call_user_func([$entity,$getterMethod]); - if (!$association) { - $association = $this->getNewInstance(); - } + $association = call_user_func([$entity,$getterMethod]); + if (!$association) { + $association = $this->getNewInstance(); + } - $value = $item[$map['fieldName']]; - unset($item[$map['fieldName']]); + $value = $item[$fieldName]; + unset($item[$fieldName]); - $this->updateEntity($value, $association); + $this->updateEntity($value, $association); - $setterMethod = sprintf('set%s',$map['fieldName']); - call_user_func([$entity, $setterMethod],$association); + $setterMethod = sprintf('set%s',$fieldName); + call_user_func([$entity, $setterMethod],$association); - $this->entityRepository = $orgRepository; - $this->entityMetadata = $orgMetadata; - $this->entityName = $orgName; - } - } + $this->entityRepository = $orgRepository; + $this->entityMetadata = $orgMetadata; + $this->entityName = $orgName; } } @@ -265,7 +277,7 @@ protected function loadAssociationObjectsToEntity(array $item, $entity) foreach ($this->entityMetadata->getAssociationMappings() as $associationMapping) { $value = null; - if (isset($item[$associationMapping['fieldName']]) && !is_object($item[$associationMapping['fieldName']])) { + if (isset($item[$associationMapping['fieldName']]) && !is_object($item[$associationMapping['fieldName']]) && !is_array($item[$associationMapping['fieldName']])) { $value = $this->entityManager->getReference($associationMapping['targetEntity'], $item[$associationMapping['fieldName']]); } @@ -312,6 +324,7 @@ protected function reEnableLogging() * Finds existing entity or create a new instance * * @param array $item + * @return mixed|null|object */ protected function findOrCreateItem(array $item) {