Adding fixtures to the DB using Symfony2 and Doctrine

So I have a DB being created automatically for me using Symfony and Doctrine. Now I want to add some initial data so that I can actually develop some code to use the DB.

We do this by using the doctrine-fixtures-bundle to create classes that will load some data into our tables.

The first thing we have to do is adding the bundle in our composer.json:

{
    "require": {
        "doctrine/doctrine-fixtures-bundle": "2.2.*"
    }
}

Next we create the folder where we will put the classes that will load data into our database:

src/MainNameSpace/Bundle/ModelBundle/DataFixtures/ORM

In this folder we create a class for each entity we have in our schema. So assuming we have entity AaaAaa and we want to put some data into its table we would do:


/** 
 * LoadAaaAaa.php 
 */

namespace MainNameSpaceBundleModelBundleDataFixturesORM; 

use DoctrineCommonDataFixturesAbstractFixture; 
use DoctrineCommonDataFixturesOrderedFixtureInterface; 
use DoctrineCommonPersistenceObjectManager; 
use MainNameSpaceBundleModelBundleEntityAaaAaa; 

class LoadAaaAaa extends AbstractFixture 
{
     /**
      * {@inheritDoc}
      */
     function load(ObjectManager $manager)
     {
        $aEntity = new AaaAaa();
        // This will allow us to get the entity created from the object manager
        //    and connect it to another entity
        $this->addReference('_AaaAaa_01_', $entity); 

        $aEntity->setName('A name');
        $aEntity->setDescription('A description ...');
        $aEntity->setDate(new DateTime());

        $manager->persist($aEntity);
        $manager->flush();
    }
}

This class is just instantiating the entity with some data, persisting it and flushing, so it actually writes the data in the DB. Of course, if we want to put more data in the DB we can make a loop instantiating and persisting entities, but lets keep it simple.

Now, we can use this to put data in all tables, but what about connecting the data that we put in the DB tables?

I like to keep things separated, so I also create a loader class for each connection.
Lets suppose now that we have again entity AaaAaa with a many to one relationship to entity BbbBbb. This means that AaaAaa will have a property pointing to an entity BbbBbb and BbbBbb will have a property which is a collection of AaaAaa.

The loader class for entity AaaAaa will be exactly the same as before, and for entity BbbBbb will be built in the same fashion. But the class that connects both entities will be slightly different:


/**
 * LoadAaaAaaBbbBbb.php
 */

//...

    /**
     * {@inheritDoc}
     */
    function load(ObjectManager $manager)
    {
        $aEntity = $manager->merge($this->getReference('_AaaAaa_01_'));
        $bEntity = $manager->merge($this->getReference('_BbbBbb_01_'));

        // Only one of the following lines is needed:
        $aEntity->setBbbBbb($bEntity); // A only has one B
        $bEntity->addAaaAaa($aEntity); // B can have several A

        $manager->persist($aEntity);
        $manager->persist($bEntity);
        $manager->flush();
    }
}

And if the relationship is many-to-many, then its basically the same, except that both entities will have collections pointing to the other entities:


/**
 * LoadAaaAaaBbbBbb.php
 */

//...

    /**
     * {@inheritDoc}
     */
    function load(ObjectManager $manager)
    {
        $aEntity = $manager->merge($this->getReference('_AaaAaa_01_'));
        $bEntity = $manager->merge($this->getReference('_BbbBbb_01_'));

        // Only one of the following lines is needed:
        $aEntity->addBbbBbb($bEntity); // A can have several B
        $bEntity->addAaaAaa($aEntity); // B can have several A

        $manager->persist($aEntity);
        $manager->persist($bEntity);
        $manager->flush();
    }
}

But now we still have a problem, Doctrine will execute the load classes in alphabetical order. So what happens if we try to load data for a connection without having yet loaded the data for all the related entities? It fails!

So what we have to do is tell Doctrine which loader classes should run first. We can do this in two ways:

  1. Implement the OrderedFixtureInterface and tell Doctrine explicitly in what order to run the loader classes;
  2. Implement the DependentFixtureInterface and tell Doctrine what loader classes should be ran before the current loader class. Doctrine will figure out the order.

If we want to explicitly tell Doctrine the order in which to run the loader classes we do this:


/**
 * LoadAaaAaaBbbBbb.php
 */

//...

class LoadAaaAaaBbbBbb extends AbstractFixture implements OrderedFixtureInterface

//...

    /**
     * Get the order of this fixture
     *
     * @return integer
     */
    function getOrder()
    {
        return 1; // number in which order to load fixtures
    }
}

If we want Doctrine to figure out the order by itself, we do this:


/**
 * LoadAaaAaaBbbBbb.php
 */

//...

class LoadAaaAaaBbbBbb extends AbstractFixture implements DependentFixtureInterface

//...

    /**
     * Get the order of this fixture
     *
     * @return array
     */
    function getDependencies()
    {
        // array with the fixture classes this fixture is dependent on
        return array('MyDataFixturesMyOtherFixture');
    }
}

When we have everything ready, we just run a Symfony/Doctrine command:

php app/console doctrine:fixtures:load -n

That’s all folks!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s