DataFixures: миграции для тестовых данных
2 ноября 2012 Symfony2 15030 просмотров
Всегда, перед тем как запустить приложение в свет, разработчики / тестировщики / заказчики работают с какими-то тестовыми данными (пользователи, заказы и т.д.). Перед запуском на каком-то сервере (test, demo ...) им нужно собрать SQL с общей структурой базы, включить в этот SQL необходимые данные, выполнить всё это на сервере. Мне нравится как к этому делу подошли Symfony 2 и Doctrine 2.

Всегда приятно иметь на сервере после обновления эталонные данные и свежую базу, ну или хотя бы просто новую структуру базы. И чтобы это было удобно, чтобы после обновления SVN нам нужно было пользоваться только одной утилитой для настройки всего приложения. Да, это app/console. По умолчанию мы уже можем проводить основные операции с базой данных, основываясь на Entity-классах Doctrine 2 ORM. Простые приятные команды:
app\console doctrine:database:drop
app\console doctrine:database:create
app\console doctrine:schema:drop
app\console doctrine:schema:create
app\console doctrine:schema:update
Всё это касается самой структуры базы. Что касается данных, Doctrine 2 и Symfony 2 предоставляют нам такой инструмент как Data Fixtures. Инструмент идет как расширение к Doctrine 2 и как Bundle в Symfony 2. Чтобы подключить к себе в проект DataFixtures, нужно добавить в файл deps следующие элементы:
[doctrine-fixtures]
    git=http://github.com/doctrine/data-fixtures.git

[DoctrineFixturesBundle]
    git=http://github.com/doctrine/DoctrineFixturesBundle.git
    target=/bundles/Symfony/Bundle/DoctrineFixturesBundle
    version=origin/2.0
bin\vendors install
Также необходимо включить Bundle в проект и добавить запись в автозагрузку. AppKernel.php:
public function registerBundles()
{
    $bundles = array(
        // ...
        new Symfony\Bundle\DoctrineFixturesBundle\DoctrineFixturesBundle(),
        // ...
    );

    // ...

    return $bundles;
}
autoload.php:
$loader->registerNamespaces(array(
    // ...
    'Doctrine\\Common\\DataFixtures' => __DIR__.'/../vendor/doctrine-fixtures/lib',
    // ...
));
Набор данных описывается в PHP-классах, реализующих интерфейс Doctrine\Common\DataFixtures\FixtureInterface. В методе load мы добавляем наши данные. В Symfony 2 классы DataFixtures необходимо размещать в папке My\TestBundle\DataFixtures\ORM.
namespace My\TestBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\FixtureInterface;
use My\TestBundle\Entity\User;

class LoadUserData implements FixtureInterface
{
    public function load(\Doctrine\Common\Persistence\ObjectManager $manager)
    {
        $newUser = new User();
        $newUser->setFirstName(‘John’);
        $newUser->setLastName(‘Lennon’);

        $manager->persist($newUser);
        $manager->flush();
    }
}
Таких классов у нас может быть несколько. Очень часто необходимо выполнять вставку в какой-то последовательности. Сразу роли пользователей, потом пользователи. Чтобы указать порядок выполнения, нам нужно реализовать интерфейс Doctrine\Common\DataFixtures\OrderedFixtureInterface и вернуть в методе getOrder порядковый номер. Для того, чтобы пользоваться новыми вставленными данными, используются методы setReference, getReference. Подробнее в двух следующих классах.
namespace My\TestBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use My\TestBundle\Entity\Role;

class LoadRoleData extends AbstractFixture implements OrderedFixtureInterface
{
    public function load(\Doctrine\Common\Persistence\ObjectManager $manager)
    {
        $adminRole = new Role();
        $adminRole->setName(‘admin’);
        $manager->persist($adminRole);

        $memberRole = new Role();
        memberRole>setName(‘member’);
        $manager->persist($memberRole);

        $manager->flush();
        $this->setReference(‘member_role’, $memberRole);
    }

    public function getOrder()
    {
        return 1;
    }
}

namespace My\TestBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use My\TestBundle\Entity\User;

class LoadRoleData extends AbstractFixture implements OrderedFixtureInterface
{
    public function load(\Doctrine\Common\Persistence\ObjectManager $manager)
    {
        $memberRole = $this->getReference(‘member_role’);

        $member = new User();
        $member->setFirstName(‘Martin’);
        $member->setLastName(‘Luther King’);
        $member->setRole($memberRole);
        $manager->persist($memberRole);

        $manager->flush();
    }


    public function getOrder()
    {
        return 2;
    }
}
Теперь после обновления кода на сервере нам нужно запустить одну команду, чтобы вставить новые тестовые данные. Перед ее запуском можете очистить базу командами doctrine:*