Skip to content

Commit

Permalink
Merge pull request #6 from webfactory/feature/4-automatically-determi…
Browse files Browse the repository at this point in the history
…ne-and-setup-dependencies

Automatically determine and setup dependencies
  • Loading branch information
Matthimatiker committed Jun 5, 2015
2 parents 4f3a35e + dc2eaeb commit f8ec175
Show file tree
Hide file tree
Showing 13 changed files with 753 additions and 53 deletions.
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,8 @@ Usage
/** @see \PHPUnit_Framework_TestCase::setUp() */
protected function setUp()
{
$this->infrastructure = new ORMInfrastructure(
array(
'Entity\MyEntity',
// recursively add all class names of associated classes
)
$this->infrastructure = ORMInfrastructure::createWithDependenciesFor(
'Entity\MyEntity'
);
$this->repository = $this->infrastructure->getRepository('Entity\MyEntity');
}
Expand Down Expand Up @@ -99,7 +96,7 @@ Testing the library itself

After installing the dependencies managed via composer, just run

php vendor/phpunit/phpunit/phpunit.php
vendor/bin/phpunit

from the library's root folder. This uses the shipped phpunit.xml.dist - feel free to create your own phpunit.xml if you
need local changes.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

/*
* (c) webfactory GmbH <info@webfactory.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Webfactory\Doctrine\ORMTestInfrastructure;

use Doctrine\Common\Cache\ArrayCache;
use Doctrine\ORM\Tools\Setup;

/**
* Creates ORM configurations for a set of entities.
*
* These configurations are meant for testing only.
*/
class ConfigurationFactory
{
/**
* Creates the ORM configuration for the given set of entities.
*
* @param string[] $entityClasses
* @return \Doctrine\ORM\Configuration
*/
public function createFor(array $entityClasses)
{
$config = Setup::createAnnotationMetadataConfiguration(
$this->getFilePathsForClassNames($entityClasses),
// Activate development mode.
true,
// Store proxies in the default temp directory.
null,
// Avoid Doctrine auto-detection of cache and use an isolated cache.
new ArrayCache(),
false
);
return $config;
}

/**
* Returns a list of file paths for the provided class names.
*
* @param string[] $classNames
* @return string[]
*/
protected function getFilePathsForClassNames(array $classNames)
{
$paths = array();
foreach ($classNames as $className) {
$paths[] = $this->getFilePathForClassName($className);
}
return array_unique($paths);
}

/**
* Returns the path to the directory that contains the given class.
*
* @param string $className
* @return string
*/
protected function getFilePathForClassName($className)
{
$info = new \ReflectionClass($className);
return dirname($info->getFileName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

/*
* (c) webfactory GmbH <info@webfactory.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Webfactory\Doctrine\ORMTestInfrastructure;

use Doctrine\Common\Persistence\Mapping\ReflectionService;
use Doctrine\Common\Persistence\Mapping\RuntimeReflectionService;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\Mapping\ClassMetadata;

/**
* Takes a set of entity classes and resolves to a set that contains all entities
* that are referenced by the provided entity classes (via associations).
*
* The resolved set also includes the original entity classes.
*/
class EntityDependencyResolver implements \IteratorAggregate
{
/**
* Contains the names of the entity classes that were initially provided.
*
* @var string[]
*/
protected $initialEntitySet = null;

/**
* Service that is used to inspect entity classes.
*
* @var ReflectionService
*/
protected $reflectionService = null;

/**
* Factory that is used to create ORM configurations.
*
* @var ConfigurationFactory
*/
protected $configFactory = null;

/**
* Creates a resolver for the given entity classes.
*
* @param string[] $entityClasses
*/
public function __construct(array $entityClasses)
{
$this->initialEntitySet = $this->normalizeClassNames($entityClasses);
$this->reflectionService = new RuntimeReflectionService();
$this->configFactory = new ConfigurationFactory();
}

/**
* Allows iterating over the set of resolved entities.
*
* @return \Traversable
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php
*/
public function getIterator()
{
return new \ArrayIterator($this->resolve($this->initialEntitySet));
}

/**
* Resolves the dependencies for the given entities.
*
* @param string[] $entityClasses
* @return string[]
*/
protected function resolve(array $entityClasses)
{
$entitiesToCheck = $entityClasses;
$config = $this->configFactory->createFor($entitiesToCheck);
while (count($associatedEntities = $this->getDirectlyAssociatedEntities($config, $entitiesToCheck)) > 0) {
$newAssociations = array_diff($associatedEntities, $entityClasses);
$entityClasses = array_merge($entityClasses, $newAssociations);
$config = $this->configFactory->createFor($entityClasses);
$entitiesToCheck = $newAssociations;
}
return $entityClasses;
}

/**
* Returns the class names of additional entities that are directly associated with
* one of the entities that is explicitly mentioned in the given configuration.
*
* @param Configuration $config
* @param string[] $entityClasses Classes whose associations are checked.
* @return string[] Associated entity classes.
*/
protected function getDirectlyAssociatedEntities(Configuration $config, $entityClasses)
{
if (count($entityClasses) === 0) {
return array();
}
$associatedEntities = array();
foreach ($entityClasses as $entityClass) {
/* @var $entityClass string */
$metadata = new ClassMetadata($entityClass);
$metadata->initializeReflection($this->reflectionService);
$config->getMetadataDriverImpl()->loadMetadataForClass($entityClass, $metadata);
foreach ($metadata->getAssociationNames() as $name) {
/* @var $name string */
$associatedEntity = $metadata->getAssociationTargetClass($name);
$associatedEntities[] = $metadata->fullyQualifiedClassName($associatedEntity);
}
}
return array_unique($associatedEntities);
}

/**
* Removes leading slashes from the given class names.
*
* @param string[] $entityClasses
* @return string[]
*/
protected function normalizeClassNames(array $entityClasses)
{
return array_map(function ($class) {
return ltrim($class, '\\');
}, $entityClasses);
}
}
2 changes: 1 addition & 1 deletion src/Webfactory/Doctrine/ORMTestInfrastructure/Importer.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ protected function importEntity($entity)
/**
* Imports a list of entities.
*
* @param array(object) $entities
* @param object[] $entities
*/
protected function importEntityList(array $entities)
{
Expand Down

0 comments on commit f8ec175

Please sign in to comment.