読者です 読者をやめる 読者になる 読者になる

10分ぐらいで学べるSymfony2 〜DataFixturesの使い方編〜

Symfony2にはDoctrineFixturesBundleというデータを投入するバンドルが存在し、コマンドからデータを投入する方法はマニュアルに記載があります。
phpunitのsetUp処理でデータを「クリア→投入」したかったのですが、やり方が見当たらなかったので調べたメモとなります。


バンドルのインストールなどは「DoctrineFixturesBundle」を参照してください。


1.DoctrineFixturesBundleの簡単な使用例

1.1 Fixtureファイルの作成

Hoge/UserBundleにFixtureを作成する場合の例となります。
バンドルの「DataFixtures/ORM」の下にfixtureファイルを置きます

# mkdir -p src/Hoge/UserBundle/DataFixtures/ORM/
# vi src/Hoge/UserBundle/DataFixtures/ORM/LoadUser1Data.php

namespace Hoge\UserBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\FixtureInterface;
use Hoge\UserBundle\Entity\User;

class LoadUserData implements FixtureInterface
{
    public function load($manager)
    {
        $user = new User();
        $user->setEmail('admin');
        $user->setPassword('test');
        $user->setSalt('hoge');

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

1.2 コマンド実行

これでコマンドを実行することでデータは投入されます
(デフォルトだと投入前のデータは消去)

$ php app/console doctrine:fixtures:load


2. 複数fixtureのデータ連携


Userテーブルの情報を他テーブルのデータロードに利用するなど複数のfixtureファイル間でのデータの連携をしたい場合の例となります。
OrderedFixtureInterfaceをimplementsしつつ、AbstractFixtureを継承してます。

# vi src/Hoge/UserBundle/DataFixtures/ORM/LoadUser2Data.php
<?php
namespace Hoge\UserBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Hoge\UserBundle\Entity\User;

class LoadUser2Data extends AbstractFixture implements OrderedFixtureInterface
{
    public function load($manager)
    {
        $user = new User();
        $user->setEmail('user1');
        $user->setPassword('test1');
        $user->setSalt('hoge1');

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

        $this->addReference('user1', $user);
    }

    public function getOrder()
    {
        return 1; // the order in which fixtures will be loaded
    }
}

ApiTokenテーブルではUserテーブルのプライマリーキーをuser_idカラムに保持する構造になってます。

# vi src/Hoge/UserBundle/DataFixtures/ORM/LoadApiTokenData.php
<?php

namespace Hoge\UserBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Hoge\UserBundle\Entity\ApiToken;

class LoadApiTokenData extends AbstractFixture implements OrderedFixtureInterface
{
    public function load($manager)
    {
        $apiToken = new ApiToken();
        $apiToken->setUserId($manager->merge($this->getReference('user1'))->getId());
        $apiToken->setToken('hoge');
        $apiToken->setExpired(new \DateTime());

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

    public function getOrder()
    {
        return 2; // the order in which fixtures will be loaded
    }
}

LoadUser2Dataで「addReference('user1', $user)」する事でLoadApiTokenDataのgetReferenceで
セットしたオブジェクトをロードできます。
また、getOrderでは読込ファイルの順序を設定できます。
例えば、LoadUser2DataのgetOrderの戻値を10と指定するとLoadApiTokenDataを先にロードしようとして
user1が定義されてないとエラーになります。

3. fixtureでコンテナを使う

ContainerAwareInterfaceをimplementsしてsetContainerを実装することで
fixture内でコンテナを扱えるようになります。

# vi src/Hoge/UserBundle/DataFixtures/ORM/LoadUser3Data.php
<?php

namespace Hoge\UserBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\FixtureInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Hoge\UserBundle\Entity\User;

class LoadUser3Data implements FixtureInterface, ContainerAwareInterface
{
    private $container;

    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }

    public function load($manager)
    {
        $user = new User();
        $user->setEmail('user3');
        $user->setSalt('hoge3');
        $encoder = $this->container->get('security.encoder_factory')->getEncoder($user);
        $user->setPassword($encoder->encodePassword('test', $user->getSalt()));

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

4. ソースコードからfixtureを呼び出し

テストファイルからfixtureを呼び出す際の処理となります。
LoadUser2Dataがテストのたびに「クリア→投入」されるようになります。

use Symfony\Bundle\DoctrineFixturesBundle\Common\DataFixtures\Loader;
use Hoge\UserBundle\DataFixtures\ORM\LoadUser2Data;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;


    public function setUp()
    {
        $kernel = static::createKernel();
        $kernel->boot();
        $loader = new Loader($kernel->getContainer());
        $loader->addFixture(new LoadUser2Data);
        $fixtures = $loader->getFixtures();
        $em = $kernel->getContainer()->get('doctrine.orm.entity_manager');
        $purger = new ORMPurger($em);
        $purger->setPurgeMode(ORMPurger::PURGE_MODE_TRUNCATE);
        $executor = new ORMExecutor($em, $purger);
        $executor->execute($fixtures);