Skip to content

Commit

Permalink
moved some orm logic where it belongs
Browse files Browse the repository at this point in the history
  • Loading branch information
digitalkaoz committed Nov 2, 2012
2 parents ef03c7a + 1242311 commit 331f3a3
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 57 deletions.
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ $persister->persist($objects);
```

> **Note**: To load plain PHP files, you can use the `\Nelmio\Alice\Loader\Base`
> class instead.
> class instead. These PHP files must return an array containing the same
> structure as the yaml files have.
## Reference ##

Expand Down Expand Up @@ -121,14 +122,23 @@ Nelmio\Entity\User:
As you see in the last line, you can also pass arguments to those just as if
you were calling a function.

#### Localized Fake Data ####

Faker can create localized data for adresses, phone numbers and so on. You can
set the default locale to use by passing a `locale` value in the `$options`
array of Fixtures::load.

Additionally, you can mix locales by adding a locale prefix to the faker key,
i.e. `<fr_FR:phoneNumber>` or `<de_DE:firstName>`.

### Optional Data ###

Some fields do not have to be filled-in, like the `favoriteNumber` in this
example might be personal data you don't want to share, to reflect this in
our fixtures and be sure the site works and looks alright even when users
don't enter a favorite number, we can make Alice fill it in *sometimes* using
the `50%? value : empty value` notation. It's a bit like the ternary operator,
and you can ommit the empty value if null is ok as such: `%50? value`.
and you can omit the empty value if null is ok as such: `%50? value`.

Let's update the user definition with this new information:

Expand Down
11 changes: 5 additions & 6 deletions src/Nelmio/Alice/Fixtures.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ class Fixtures
/**
* Loads a fixture file into an object container
*
* @param string|array $file filename to load data from or data array
* @param string|array $file filename to load data from or data array
* @param object $container object container
* @param array $options available options:
* @param array $options available options:
* - providers: an array of additional faker providers
* - locale: the faker locale
*/
Expand All @@ -37,21 +37,20 @@ public static function load($file, $container, array $options = array())
} elseif ((is_string($file) && preg_match('{\.php$}', $file)) || is_array($file)) {
$loader = new Loader\Base($options['locale'], $options['providers']);
} else {
throw new \InvalidValueException('Unknown file/data type: '.gettype($file).' ('.json_encode($file).')');
throw new \InvalidArgumentException('Unknown file/data type: '.gettype($file).' ('.json_encode($file).')');
}

if ($container instanceof ObjectManager) {
$persister = new ORM\Doctrine($container);
} else {
throw new \InvalidValueException('Unknown container type '.get_class($container));
throw new \InvalidArgumentException('Unknown container type '.get_class($container));
}

$loader->setObjectManager($container);
$loader->setORM($persister);

$objects = $loader->load($file);
$persister->persist($objects);

return $objects;

}
}
102 changes: 66 additions & 36 deletions src/Nelmio/Alice/Loader/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@

namespace Nelmio\Alice\Loader;

use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Yaml\Yaml as YamlParser;
use Symfony\Component\Form\Util\FormUtil;
use Nelmio\Alice\LoaderInterface;
use Nelmio\Alice\ORMInterface;
use Nelmio\Alice\LoaderInterface;

/**
* Loads fixtures from an array or php file
Expand All @@ -41,16 +39,39 @@ class Base implements LoaderInterface
{
protected $references = array();
/**
* @var ObjectManager
* @var ORMInterface
*/
protected $manager;

/**
* @var \Faker\Generator[]
*/
private $generators;

/**
* Default locale to use with faker
*
* @var string
*/
private $defaultLocale;

/**
* Custom faker providers to use with faker generator
*
* @var array
*/
private $providers;

/**
* @param string $locale default locale to use with faker if none is
* specified in the expression
* @param array $providers custom faker providers in addition to the default
* ones from faker
*/
public function __construct($locale = 'en_US', array $providers = array())
{
$this->generator = \Faker\Factory::create($locale);
foreach ($providers as $provider) {
$this->generator->addProvider($provider);
}
$this->defaultLocale = $locale;
$this->providers = $providers;
}

/**
Expand Down Expand Up @@ -111,12 +132,35 @@ public function getReferences()
return $this->references;
}

public function fake($formatter, $arg = null, $arg2 = null, $arg3 = null)
public function fake($formatter, $locale = null, $arg = null, $arg2 = null, $arg3 = null)
{
$args = func_get_args();
array_shift($args);
array_shift($args);

return $this->generator->format($formatter, $args);
return $this->getGenerator($locale)->format($formatter, $args);
}

/**
* Get the generator for this locale
*
* @param string $locale the requested locale, defaults to constructor injected default
*
* @return \Faker\Generator the generator for the requested locale
*/
private function getGenerator($locale = null)
{
$locale = $locale ?: $this->defaultLocale;

if (!isset($this->generators[$locale])) {
$generator = \Faker\Factory::create($locale);
foreach ($this->providers as $provider) {
$generator->addProvider($provider);
}
$this->generators[$locale] = $generator;
}

return $this->generators[$locale];
}

private function createObject($class, $name, $data)
Expand Down Expand Up @@ -166,31 +210,12 @@ private function checkForEntity($obj, $method, $value)
$params = $reflection->getParameters();

if ($params[0]->getClass() && is_numeric($value)) {
$value = $this->findEntity($params[0]->getClass()->getName(), $value);
$value = $this->manager->find($params[0]->getClass()->getName(), $value);
}

return $value;
}

/**
* if provided an integer and the setter needs an Object, try to find it with the OM
*
* @param string $class the class to find
* @param int $id the entity id
* @return object
* @throws \UnexpectedValueException
*/
private function findEntity($class, $id)
{
$entity = $this->manager->find($class, $id);

if (!$entity) {
throw new \UnexpectedValueException('Entity for Id ' . $id . ' and Class ' . $class . ' not found');
}

return $entity;
}

private function process($data, array $variables)
{
if (is_array($data)) {
Expand Down Expand Up @@ -233,29 +258,34 @@ private function process($data, array $variables)
$that = $this;
// replaces a placeholder by the result of a ->fake call
$replacePlaceholder = function ($matches) use ($variables, $that) {
$args = (!empty($matches['args']) ? ', '.$matches['args'] : '');
$args = !empty($matches['args']) ? $matches['args'] : null;

if (!$args) {
return $that->fake($matches['name']);
return $that->fake($matches['name'], $matches['locale']);
}

// replace references to other variables in the same object
$args = preg_replace_callback('{\{?\$([a-z0-9_]+)\}?}i', function ($match) use ($variables) {
if (isset($variables[$match[1]])) {
return '$variables['.var_export($match[1], true).']';
}

return $match[0];
}, $args);

return eval('return $that->fake('.var_export($matches['name'], true) . $args.');');
$locale = var_export($matches['locale'], true);
$name = var_export($matches['name'], true);

return eval('return $that->fake(' . $name . ', ' . $locale . ', ' . $args . ');');
};

// format placeholders without preg_replace if there is only one to avoid __toString() being called
if (preg_match('#^<(?<name>[a-z0-9_]+?)(?:\((?<args>.+?)\))?>$#i', $data, $matches)) {
$placeHolderRegex = '<(?:(?<locale>[a-z]+(?:_[a-z]+)?):)?(?<name>[a-z0-9_]+?)(?:\((?<args>.+?)\))?>';
if (preg_match('#^'.$placeHolderRegex.'$#i', $data, $matches)) {
$data = $replacePlaceholder($matches);
} else {
// format placeholders inline
$data = preg_replace_callback('#<(?<name>[a-z0-9_]+?)(?:\((?<args>.+?)\))?>#i', function ($matches) use ($replacePlaceholder) {
$data = preg_replace_callback('#'.$placeHolderRegex.'#i', function ($matches) use ($replacePlaceholder) {
return $replacePlaceholder($matches);
}, $data);
}
Expand Down Expand Up @@ -324,7 +354,7 @@ private function findAdderMethod($obj, $key)
}
}

public function setObjectManager(ObjectManager $manager)
public function setORM(ORMInterface $manager)
{
$this->manager = $manager;
}
Expand Down
3 changes: 0 additions & 3 deletions src/Nelmio/Alice/Loader/Yaml.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@

namespace Nelmio\Alice\Loader;

use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Yaml\Yaml as YamlParser;
use Nelmio\Alice\LoaderInterface;
use Nelmio\Alice\ORMInterface;

/**
* Loads fixtures from a yaml file
Expand Down
4 changes: 2 additions & 2 deletions src/Nelmio/Alice/LoaderInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ interface LoaderInterface
/**
* Loads a fixture file
*
* @param string $file filename
* @param string $file filename
*/
public function load($file);

/**
* Returns a reference to a fixture by name
*
* @param string $name
* @param string $name
* @return object
*/
public function getReference($name);
Expand Down
18 changes: 18 additions & 0 deletions src/Nelmio/Alice/ORM/Doctrine.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,22 @@ public function persist(array $objects)
$this->om->flush();
}
}

/**
* finds an entity by class and id
*
* @param string $class
* @param int $id
* @return mixed
*/
public function find($class, $id)
{
$entity = $this->om->find($class, $id);

if (!$entity) {
throw new \UnexpectedValueException('Entity with Id ' . $id . ' and Class ' . $class . ' not found');
}

return $entity;
}
}
7 changes: 7 additions & 0 deletions src/Nelmio/Alice/ORMInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,11 @@ interface ORMInterface
* @param array[object] $objects instance to persist in the DB
*/
public function persist(array $objects);

/**
* @param string $class
* @param int $id
* @return mixed
*/
public function find($class, $id);
}
6 changes: 3 additions & 3 deletions tests/Nelmio/Alice/FixturesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class FixturesTest extends \PHPUnit_Framework_TestCase
const USER = 'Nelmio\Alice\fixtures\User';
const GROUP = 'Nelmio\Alice\fixtures\Group';

public function testLoadYamlFilesAndDoctrineORM()
public function testLoadLoadsYamlFilesAndDoctrineORM()
{
$om = $this->getDoctrineManagerMock(13);
$objects = Fixtures::load(__DIR__.'/fixtures/complete.yml', $om);
Expand All @@ -35,7 +35,7 @@ public function testLoadYamlFilesAndDoctrineORM()
$this->assertCount(3, $group->getMembers());
}

public function testLoadArrays()
public function testLoadLoadsArrays()
{
$om = $this->getDoctrineManagerMock(2);

Expand All @@ -62,7 +62,7 @@ public function testLoadArrays()
$this->assertEquals(42, $user->favoriteNumber);
}

public function testLoadPHPfiles()
public function testLoadLoadsPHPfiles()
{
$om = $this->getDoctrineManagerMock(2);

Expand Down
32 changes: 30 additions & 2 deletions tests/Nelmio/Alice/Loader/BaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ public function testLoadParsesFakerDataMultiple()
));

$this->assertNotEquals('<firstName> <lastName>', $res[0]->username);
$this->assertRegExp('{^[a-z]+ [a-z]+$}i', $res[0]->username);
$this->assertRegExp('{^[\w\']+ [\w\']+$}i', $res[0]->username);
}

public function testLoadParsesFakerDataWithArgs()
Expand Down Expand Up @@ -346,6 +346,34 @@ public function testLoadParsesVariables()
$this->assertLessThanOrEqual(strtotime("-9days"), $res[0]->fullname->getTimestamp());
}

public function testLoadParsesFakerDataWithLocale()
{
$res = $this->loadData(array(
self::USER => array(
'user0' => array(
'username' => '<fr_FR:siren>',
),
),
));

$this->assertRegExp('{^\d{3} \d{3} \d{3}$}', $res[0]->username);
}

/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage Unknown formatter "siren"
*/
public function testLoadParsesFakerDataUsesDefaultLocale()
{
$res = $this->loadData(array(
self::USER => array(
'user0' => array(
'username' => '<siren>',
),
),
));
}

public function testLoadCreatesInclusiveRangesOfObjects()
{
$res = $this->loadData(array(
Expand Down Expand Up @@ -412,4 +440,4 @@ public function fooGenerator()
{
return 'foo';
}
}
}
Loading

0 comments on commit 331f3a3

Please sign in to comment.