Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow dependent fixtures to reference other types of fixture without triggering an error. #131

Open
wants to merge 11 commits into
base: 1.5.x
Choose a base branch
from
198 changes: 109 additions & 89 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,109 +4,125 @@ This extension aims to provide a simple way to manage and execute the loading of
for the Doctrine ORM or ODM. You can write fixture classes by implementing the
Doctrine\Common\DataFixtures\FixtureInterface interface:

namespace MyDataFixtures;
```php
namespace MyDataFixtures;

use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\FixtureInterface;

class LoadUserData implements FixtureInterface
class LoadUserData implements FixtureInterface
{
public function load(ObjectManager $manager)
{
public function load(ObjectManager $manager)
{
$user = new User();
$user->setUsername('jwage');
$user->setPassword('test');

$manager->persist($user);
$manager->flush();
}
$user = new User();
$user->setUsername('jwage');
$user->setPassword('test');

$manager->persist($user);
$manager->flush();
}
}
```

Now you can begin adding the fixtures to a loader instance:

use Doctrine\Common\DataFixtures\Loader;
use MyDataFixtures\LoadUserData;
```php
use Doctrine\Common\DataFixtures\Loader;
use MyDataFixtures\LoadUserData;

$loader = new Loader();
$loader->addFixture(new LoadUserData);
$loader = new Loader();
$loader->addFixture(new LoadUserData);
```

You can load a set of fixtures from a directory as well:

$loader->loadFromDirectory('/path/to/MyDataFixtures');
```php
$loader->loadFromDirectory('/path/to/MyDataFixtures');
```

You can get the added fixtures using the getFixtures() method:

$fixtures = $loader->getFixtures();
```php
$fixtures = $loader->getFixtures();
```

Now you can easily execute the fixtures:

use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
```php
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;

$purger = new ORMPurger();
$executor = new ORMExecutor($em, $purger);
$executor->execute($loader->getFixtures());
$purger = new ORMPurger();
$executor = new ORMExecutor($em, $purger);
$executor->execute($loader->getFixtures());
```

If you want to append the fixtures instead of purging before loading then pass true
to the 2nd argument of execute:

$executor->execute($loader->getFixtures(), true);
```php
$executor->execute($loader->getFixtures(), true);
```

## Sharing objects between fixtures

In case if fixture objects have relations to other fixtures, it is now possible
to easily add a reference to that object by name and later reference it to form
a relation. Here is an example fixtures for **Role** and **User** relation

namespace MyDataFixtures;
```php
namespace MyDataFixtures;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\Persistence\ObjectManager;

class LoadUserRoleData extends AbstractFixture
class LoadUserRoleData extends AbstractFixture
{
public function load(ObjectManager $manager)
{
public function load(ObjectManager $manager)
{
$adminRole = new Role();
$adminRole->setName('admin');
$adminRole = new Role();
$adminRole->setName('admin');

$anonymousRole = new Role;
$anonymousRole->setName('anonymous');
$anonymousRole = new Role;
$anonymousRole->setName('anonymous');

$manager->persist($adminRole);
$manager->persist($anonymousRole);
$manager->flush();
$manager->persist($adminRole);
$manager->persist($anonymousRole);
$manager->flush();

// store reference to admin role for User relation to Role
$this->addReference('admin-role', $adminRole);
}
// store reference to admin role for User relation to Role
$this->addReference('admin-role', $adminRole);
}
}
```

And the **User** data loading fixture:

namespace MyDataFixtures;
```php
namespace MyDataFixtures;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\Persistence\ObjectManager;

class LoadUserData extends AbstractFixture
class LoadUserData extends AbstractFixture
{
public function load(ObjectManager $manager)
{
public function load(ObjectManager $manager)
{
$user = new User();
$user->setUsername('jwage');
$user->setPassword('test');
$user->setRole(
$this->getReference('admin-role') // load the stored reference
);

$manager->persist($user);
$manager->flush();

// store reference of admin-user for other Fixtures
$this->addReference('admin-user', $user);
}
$user = new User();
$user->setUsername('jwage');
$user->setPassword('test');
$user->setRole(
$this->getReference('admin-role') // load the stored reference
);

$manager->persist($user);
$manager->flush();

// store reference of admin-user for other Fixtures
$this->addReference('admin-user', $user);
}
}
```

## Fixture ordering
**Notice** that the fixture loading order is important! To handle it manually
Expand All @@ -116,49 +132,53 @@ implement one of the following interfaces:

Set the order manually:

namespace MyDataFixtures;
```php
namespace MyDataFixtures;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;

class MyFixture extends AbstractFixture implements OrderedFixtureInterface
{
public function load(ObjectManager $manager)
{}
class MyFixture extends AbstractFixture implements OrderedFixtureInterface
{
public function load(ObjectManager $manager)
{}

public function getOrder()
{
return 10; // number in which order to load fixtures
}
public function getOrder()
{
return 10; // number in which order to load fixtures
}
}
```

### DependentFixtureInterface

Provide an array of fixture class names:

namespace MyDataFixtures;
```php
namespace MyDataFixtures;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;

class MyFixture extends AbstractFixture implements DependentFixtureInterface
{
public function load(ObjectManager $manager)
{}

public function getDependencies()
{
return array('MyDataFixtures\MyOtherFixture'); // fixture classes fixture is dependent on
}
}
class MyFixture extends AbstractFixture implements DependentFixtureInterface
{
public function load(ObjectManager $manager)
{}

class MyOtherFixture extends AbstractFixture
public function getDependencies()
{
public function load(ObjectManager $manager)
{}
return array('MyDataFixtures\MyOtherFixture'); // fixture classes fixture is dependent on
}
}

class MyOtherFixture extends AbstractFixture
{
public function load(ObjectManager $manager)
{}
}
```

**Notice** the ordering is relevant to Loader class.

Expand Down
5 changes: 4 additions & 1 deletion lib/Doctrine/Common/DataFixtures/AbstractFixture.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,14 @@ public function setReference($name, $object)
/**
* Set the reference entry identified by $name
* and referenced to managed $object. If $name
* already is set, it overrides it
* already is set, it throws a
* BadMethodCallException exception
*
* @param string $name
* @param object $object - managed object
* @see Doctrine\Common\DataFixtures\ReferenceRepository::addReference
* @throws BadMethodCallException - if repository already has
* a reference by $name
* @return void
*/
public function addReference($name, $object)
Expand Down
4 changes: 2 additions & 2 deletions lib/Doctrine/Common/DataFixtures/FixtureInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ interface FixtureInterface
/**
* Load data fixtures with the passed EntityManager
*
* @param Doctrine\Common\Persistence\ObjectManager $manager
* @param ObjectManager $manager
*/
function load(ObjectManager $manager);
public function load(ObjectManager $manager);
}
2 changes: 1 addition & 1 deletion lib/Doctrine/Common/DataFixtures/Loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ private function getUnsequencedClasses($sequences, $classes = null)
}

foreach ($classes as $class) {
if ($sequences[$class] === -1) {
if (array_key_exists($class, $sequences) && ($sequences[$class] === -1)) {
$unsequencedClasses[] = $class;
}
}
Expand Down
6 changes: 4 additions & 2 deletions lib/Doctrine/Common/DataFixtures/ProxyReferenceRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* Allow data fixture references and identities to be persisted when cached data fixtures
* are pre-loaded, for example, by LiipFunctionalTestBundle\Test\WebTestCase loadFixtures().
*
* @author Guilherme Blanco <[email protected]>
* @author Anthon Pang <[email protected]>
*/
class ProxyReferenceRepository extends ReferenceRepository
Expand Down Expand Up @@ -59,12 +60,13 @@ protected function getRealClass($className)
*/
public function serialize()
{
$unitOfWork = $this->getManager()->getUnitOfWork();
$simpleReferences = array();

foreach ($this->getReferences() as $name => $reference) {
$className = $this->getRealClass(get_class($reference));

$simpleReferences[$name] = array($className, $reference->getId());
$simpleReferences[$name] = array($className, $this->getIdentifier($reference, $unitOfWork));
}

$serializedData = json_encode(array(
Expand All @@ -90,7 +92,7 @@ public function unserialize($serializedData)
$name,
$this->getManager()->getReference(
$proxyReference[0], // entity class name
$proxyReference[1] // id
$proxyReference[1] // identifiers
)
);
}
Expand Down
11 changes: 10 additions & 1 deletion lib/Doctrine/Common/DataFixtures/ReferenceRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,23 @@ public function __construct(ObjectManager $manager)
* @param object $reference Reference object
* @param object $uow Unit of work
*
* @return mixed
* @return array
*/
protected function getIdentifier($reference, $uow)
{
// In case Reference is not yet managed in UnitOfWork
if ( ! $uow->isInIdentityMap($reference)) {
$class = $this->manager->getClassMetadata(get_class($reference));

return $class->getIdentifierValues($reference);
}

// Dealing with ORM UnitOfWork
if (method_exists($uow, 'getEntityIdentifier')) {
return $uow->getEntityIdentifier($reference);
}

// ODM UnitOfWork
return $uow->getDocumentIdentifier($reference);
}

Expand Down