Created
March 3, 2015 16:57
-
-
Save Ocramius/e2f020b0f56bc10a637a to your computer and use it in GitHub Desktop.
Simple array-based fixture loader for Doctrine ORM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
return [ | |
'Foo' = [ | |
'Foo#1' => [ | |
'bar' => 1, | |
'baz' => 2, | |
'tab' => '@Bar#1', | |
], | |
'Foo#2' => [ | |
'bar' => 1, | |
'baz' => 2, | |
'tab' => '@Foo#1', | |
], | |
], | |
'Bar' = [ | |
'Bar#1' => [ | |
'a' => 1, | |
'b' => 2, | |
], | |
] | |
]; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace FixtureLoader; | |
use Doctrine\Common\Persistence\Mapping\ClassMetadata; | |
use Doctrine\Common\Persistence\ObjectManager; | |
use Doctrine\Instantiator\Instantiator; | |
class SimpleFixtureLoader | |
{ | |
/** | |
* @var ObjectManager | |
*/ | |
private $objectManager; | |
/** | |
* @var Instantiator | |
*/ | |
private $instantiator; | |
public function __construct(ObjectManager $objectManager) | |
{ | |
$this->objectManager = $objectManager; | |
$this->instantiator = new Instantiator(); | |
} | |
public function loadFromArray(array $fixtureData) | |
{ | |
$instancesByClass = []; | |
foreach ($fixtureData as $className => $classInstancesData) { | |
$instancesByClass[$className] = $this->loadClassInstances($className, $classInstancesData); | |
} | |
return call_user_func_array( | |
'array_merge', | |
array_merge( | |
[[]], // empty array, or `array_merge` won't work with < 1 elements to merge | |
array_map( | |
function ($classInstances) use ($instancesByClass) { | |
return $this->loadClassData($classInstances, $instancesByClass); | |
}, | |
$instancesByClass | |
) | |
) | |
); | |
} | |
/** | |
* @param string $className | |
* @param array[] $instancesData | |
* | |
* @return object[][]|array[][] | |
*/ | |
private function loadClassInstances($className, array $instancesData) | |
{ | |
$metadata = $this->objectManager->getClassMetadata($className); | |
$className = $metadata->getName(); | |
$properties = $this->getReflectionPropertiesByName($metadata); | |
return array_map( | |
function (array $instanceData) use ($metadata, $className, $properties) { | |
return [ | |
'object' => $this->instantiator->instantiate($className), | |
'data' => $instanceData, | |
'className' => $className, | |
'metadata' => $metadata, | |
'properties' => $properties, | |
]; | |
}, | |
$instancesData | |
); | |
} | |
/** | |
* @param ClassMetadata $metadata | |
* | |
* @return \ReflectionProperty[] all accessible and indexed by property name | |
*/ | |
private function getReflectionPropertiesByName(ClassMetadata $metadata) | |
{ | |
$propertiesByName = []; | |
foreach ($this->getInheritanceClasses($metadata->getReflectionClass()) as $reflectionClass) { | |
foreach ($reflectionClass->getProperties() as $reflectionProperty) { | |
$reflectionProperty->setAccessible(true); | |
$propertiesByName[$reflectionProperty->getName()] = $reflectionProperty; | |
} | |
} | |
return $propertiesByName; | |
} | |
private function loadClassData(array $classInstances, array $instancesByClassName) | |
{ | |
return array_values(array_map( | |
function (array $instanceData) use ($instancesByClassName) { | |
return $this->populateInstance($instanceData, $instancesByClassName); | |
}, | |
$classInstances | |
)); | |
} | |
/** | |
* @param array $instanceData | |
* @param array $instancesByClassName | |
* | |
* @return object | |
*/ | |
private function populateInstance(array $instanceData, array $instancesByClassName) | |
{ | |
/* @var $instance object */ | |
$instance = $instanceData['object']; | |
/* @var $metadata \Doctrine\Common\Persistence\Mapping\ClassMetadata */ | |
$metadata = $instanceData['metadata']; | |
/* @var $properties \ReflectionProperty[] */ | |
$properties = $instanceData['properties']; | |
foreach ($instanceData['data'] as $field => $value) { | |
$this->populateObjectField($instance, $field, $value, $metadata, $properties, $instancesByClassName); | |
} | |
return $instance; | |
} | |
/** | |
* @param object $object | |
* @param string $field | |
* @param mixed $value | |
* @param ClassMetadata $metadata | |
* @param \ReflectionProperty[] $reflectionProperties | |
* @param object[][] $instancesByClassName indexed by class and reference name | |
*/ | |
private function populateObjectField( | |
$object, | |
$field, | |
$value, | |
ClassMetadata $metadata, | |
array $reflectionProperties, | |
array $instancesByClassName | |
) { | |
if (! isset($reflectionProperties[$field])) { | |
throw new \UnexpectedValueException(sprintf( | |
'Reflection property for field "%s" could not be found', | |
$field | |
)); | |
} | |
if ($metadata->hasField($field)) { | |
$reflectionProperties[$field]->setValue($object, $value); | |
return; | |
} | |
if (! $metadata->hasAssociation($field)) { | |
throw new \UnexpectedValueException(sprintf( | |
'No field or association found for data entry "%s"', | |
$field | |
)); | |
} | |
if (! $metadata->isCollectionValuedAssociation($field)) { | |
$reflectionProperties[$field]->setValue($object, $this->resolveReference($value, $instancesByClassName)); | |
return; | |
} | |
if (! is_array($value)) { | |
throw new \UnexpectedValueException(sprintf( | |
'Value for association "%s" was expected to be an array of references, %s given', | |
$field, | |
is_object($value) ? get_class($value) : gettype($value) | |
)); | |
} | |
$reflectionProperties[$field]->setValue( | |
$object, | |
array_map( | |
function ($reference) use ($instancesByClassName) { | |
return $this->resolveReference($reference, $instancesByClassName); | |
}, | |
$value | |
) | |
); | |
} | |
/** | |
* @param string $reference | |
* @param object[][] $instancesByClassName indexed by class and reference name | |
* | |
* @return object | |
* | |
* @throws \UnexpectedValueException | |
*/ | |
private function resolveReference($reference, array $instancesByClassName) | |
{ | |
if (null === $reference) { | |
return null; | |
} | |
list($className, $identifier) = explode('#', ltrim($reference, '@'), 2); | |
if (! isset($instancesByClassName[$className][$className . '#' . $identifier]['object'])) { | |
throw new \UnexpectedValueException(sprintf('Reference "%s" could not be resolved', $reference)); | |
} | |
return $instancesByClassName[$className][$className . '#' . $identifier]['object']; | |
} | |
/** | |
* @param \ReflectionClass $class | |
* | |
* @return \ReflectionClass[] | |
*/ | |
private function getInheritanceClasses(\ReflectionClass $class) | |
{ | |
if (! $parent = $class->getParentClass()) { | |
return [$class]; | |
} | |
return array_merge([$class], $this->getInheritanceClasses($parent)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment