Buy

Sharing Data between Fixture Classes

Let’s update the fixtures so that each event has an owner.

We have two fixture classes: one that loads events and one that loads users.

Ordering how Fixtures are Loaded

Start in the LoadUsers class. Now that events depend on users, we’ll want this fixture class to be executed before the events class. To force this, implement a new interface called OrderedFixtureInterface. This requires one method called getOrder. Let’s return 10:

// src/Yoda/UserBundle/DataFixtures/ORM/LoadUsers.php
// ...

use Doctrine\Common\DataFixtures\OrderedFixtureInterface;

class LoadUsers implements FixtureInterface, ContainerAwareInterface, OrderedFixtureInterface
{
    // ...

    public function getOrder()
    {
        return 10;
    }
}

Head over to LoadEvents and make the same change, except returning 20 so that the class is run second:

// src/Yoda/EventBundle/DataFixtures/ORM/LoadEvents.php
// ...

use Doctrine\Common\DataFixtures\OrderedFixtureInterface;

class LoadEvents implements FixtureInterface, OrderedFixtureInterface
{
    // ...

    public function getOrder()
    {
        return 20;
    }
}

Assigning Owners in Fixtures

Now, we just need to get our new User objects inside LoadEvents. DoctrineFixturesBundle has a standard way of sharing data between fixtures, but a much easier way is just to query for our wayne user:

// src/Yoda/EventBundle/DataFixtures/ORM/LoadEvents.php
// ...

class LoadEvents implements FixtureInterface, OrderedFixtureInterface
{
    $wayne = $manager->getRepository('UserBundle:User')
        ->findOneByUsernameOrEmail('wayne');

    // ...
}

All we need to do now is call setOwner on both events so that it looks like wayne created them:

// src/Yoda/EventBundle/DataFixtures/ORM/LoadEvents.php
// ...
public function load(ObjectManager $manager)
{
    $wayne = $manager->getRepository('UserBundle:User')
        ->findOneByUsernameOrEmail('wayne');
    // ...

    $event1->setOwner($wayne);
    $event2->setOwner($wayne);

    // ...
    $manager->flush();
}

Ok! Reload the fixtures!

php app/console doctrine:fixtures:load

Now use app/console to check that each event has an owner:

php app/console doctrine:query:sql "SELECT * FROM yoda_event"

Leave a comment!

  • 2016-11-03 weaverryan

    Yo Max!

    This is a good find, and a pretty common - sometimes annoying "feature" in Doctrine. By default, all relationships have ON DELETE RESTRICT in the database: if you try to delete a User, but it is the "owner" of an Event... then you'll get this error. So, you have the right fix (well, that or "CASCADE"), as long as it fits your situation (I'd say, in this case, as long as knowing the $owner of an Event isn't mission critical, then this is cool. Otherwise, you might want CASCADE).

    Cheers!

  • 2016-11-01 Max

    in Symfony 2.8 I had to add
    @ORM\JoinColumn(onDelete="SET NULL")
    To the $owner variable.

    Before a SQL error appeared:

    [Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException]
    An exception occurred while executing 'DELETE FROM yoda_user':
    SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or upda
    te a parent row: a foreign key constraint fails (`symfony`.`yoda_event`, CO
    NSTRAINT `FK_133826D97E3C61F9` FOREIGN KEY (`owner_id`) REFERENCES `yoda_us
    er` (`id`))

    Just in case someone else comes across this problem...

  • 2015-05-28 Michael Sypes

    Thanks! That's a great explanation.

  • 2015-05-28 weaverryan

    Hey Michael!

    That wasn't on purpose, but it makes for a great question :). The answer is: it doesn't matter (but it does need to be before flush). When you call persist(), it basically tells Doctrine: "Hey, be aware of this object. Later, if/when someone calls flush(), you should check out this object and save it to the database". Using persist doesn't put a "snapshot" of it in Doctrine at that time, it more just lets Doctrine know that it should be saved, once the time comes.

    Cheers!

  • 2015-05-28 Michael Sypes

    Shouldn't the call to setOwner() have to occur before the persist()? If not, why?