Фикстуры. DoctrineFixturesBundle

Фикстуры (англ. fixtures) - очень полезный инструмент для разработки. По сути, это просто набор тестовых данных, которые используются в dev-режиме. Для prod режима обычно не используются (для прода обычно используют Data Migrations).

Для работы с фикстурами в Symfony существует несколько удобных бандлов. Первый, базовый - DoctrineFixturesBundle, которому и посвящена эта заметка.

Установка: 

composer require "doctrine/doctrine-fixtures-bundle:2.2.0"

(Указана последняя стабильная на момент написания статьи версия)

Регистрируем бандл в app/AppKernel.php

if (in_array($this->getEnvironment(), array('dev', 'test'))) {
...
    $bundles[] = new DoctrineBundleFixturesBundleDoctrineFixturesBundle();
..
}

Использование (взято из документации Symfony) на примере создания тестового пользователя:

// src/Acme/HelloBundle/DataFixtures/ORM/LoadUserData.php

namespace AcmeHelloBundleDataFixturesORM;

use DoctrineCommonDataFixturesFixtureInterface; use DoctrineCommonPersistenceObjectManager; use AcmeHelloBundleEntityUser;

class LoadUserData implements FixtureInterface { /** * {@inheritDoc} */ public function load(ObjectManager $manager) { $userAdmin = new User(); $userAdmin->setUsername('admin'); $userAdmin->setPassword('test');

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

Нужной и интересной особенностью есть возможность использовать референсы. Это необходимо случаи связи между загружаемыми сущностями. Для этого в фикстуре используется метод addReference('reference', $entity), который регистрирует референс на сущность. Тогда в зависимой сущности можно будет использовать getReference('reference') - и это значение примет сеттер сущности. При этом нужно управлять порядком загрузки фикстур (чтобы референс гарантировано добавлялся до попытки использования). Модифицируем пример выше (снова согласно документации Symfony, но с некоторыми изменениями):

Сначала добавим сущность группы:

// src/Acme/HelloBundle/DataFixtures/ORM/LoadGroupData.php

namespace AcmeHelloBundleDataFixturesORM;

use DoctrineCommonDataFixturesAbstractFixture; use DoctrineCommonDataFixturesOrderedFixtureInterface; use DoctrineCommonPersistenceObjectManager; use AcmeHelloBundleEntityGroup;

class LoadGroupData extends AbstractFixture implements OrderedFixtureInterface { /** * {@inheritDoc} */ public function load(ObjectManager $manager) { $groupAdmin = new Group(); $groupAdmin->setGroupName('admin'); $this->addReference('admin-group', $groupAdmin);

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

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

Теперь, когда у нас загружена группа (и мы указали, что она должна загрузиться раньше), можно приступить к загрузке пользователя:

// src/Acme/HelloBundle/DataFixtures/ORM/LoadUserData.php
namespace AcmeHelloBundleDataFixturesORM;

use DoctrineCommonDataFixturesAbstractFixture; use DoctrineCommonDataFixturesOrderedFixtureInterface; use DoctrineCommonPersistenceObjectManager; use AcmeHelloBundleEntityUser;

class LoadUserData extends AbstractFixture implements OrderedFixtureInterface { /** * {@inheritDoc} */ public function load(ObjectManager $manager) { $userAdmin = new User(); $userAdmin->setUsername('admin'); $userAdmin->setPassword('test'); $userAdmin->setGroup($this->getReference('admin-group')); $manager->persist($userAdmin); $manager->flush(); }

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

Группу получили по референсу.

Для простого примера достаточно.  Но как лучше поступить, если, например, нужно указать пользователю несколько групп? Можно, написать, как-то вроде так:

$userAdmin->setGroup(array(
    $this->getReference('admin-group'),
    $this->getReference('other-group'),
    $this->getReference('one-more-other-group'),
));

Для этого, естественно должны быть раньше загружены other-group и one-more-other-group и созданы референсы. Но, наверное, удобнее будет реализовать метод, что-то вроде:

    /**
     * @param array $references
     * @return ArrayCollection
     */
    public function getArrayReference(array $references = array(), $prefix = null)
    {
        $outputReferences = new ArrayCollection();

foreach ($references as $reference) { $outputReferences->add($this->getReference($prefix . $reference)); }

return $outputReferences; }

Тогда сможем так использовать:

app/console doctrine:fixtures:load
$userAdmin->setGroup($this->getArrayReferences(array(
   'admin-group',
   'other-group',
   'one-more-other-group'
));

Ну и на последок, чтобы загрузить фикстуры, используем: