Doctrine Fixtures – sposoby na dane

Doctrine fixtures to dla mnie coś naturalnego, z czym stykam się na co dzień w pracy. Bardzo zdziwiłam się, że nigdzie nie mogłam znaleźć nawet ich polskiej definicji. Bardzo mnie ciekawi, z czego to wynika, czy tak rzadko są wykorzystywane, czy wręcz przeciwnie, są tak oczywiste, że nikt nie porusza tego tematu.

Definicja

Fixtures are used to load a controlled set of data into a database. This data can be used for testing or could be the initial data required for the application to run smoothly. Symfony has no built in way to manage fixtures but Doctrine2 has a library to help you write fixtures for the Doctrine ORM or ODM.

To jedyna definicja, którą udało mi się znaleźć, została zaczerpnięta bezpośrednio z dokumentacji Symfony i polecanych bundli. Myślę, że jest ona najlepsza nie tylko z powoju jej unikalności w sieci, ale dlatego, że wyczerpuje temat. Doctrine fixtures to bardzo fajne narzędzie, jednak tak jak sama definicja mówi, powinny być używane do wypełniania danych w celu odpaleniu na nich testów. Mogą też służyć jako dane inicjujące przy stawianiu kolejnych instancji aplikacji, czy przy automatyzacji stacji lokalnych. Osobiście używam ich w obu wymienionych sytuacjach. Jednak jest małe ale. W momencie, gdy wdrażamy wersję produkcyjną, radziłabym dane inicjujące przenieść do migracji tak jak cały system wdrażania zmian na bazie, a fixtury traktować jako dane testowe.

Doctrine Fixtures Bundle

Sama instalacja przebiega standardowo:

composer require --dev doctrine/doctrine-fixtures-bundle
class AppKernel extends Kernel
{
    public function registerBundles()
    {
        // ...
        if (in_array($this->getEnvironment(), array('dev', 'test'))) {
            $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
        }

        return $bundles
    }
}

Po chwili możemy już praktycznie działać i tworzyć fixtury.  Do tego celu używamy plików php. W sieci są również bundle, które umożliwiają na definiowanie fixtur w plikach yml. W jednym ze swoich projektów korzystam właśnie z jednego z nich. Oba podejścia mają swoje wady i zalety. Każdy ma jakieś ograniczenia, musisz sam sprawdzić, co dla Ciebie będzie lepsze.

Gdy już zainstalujemy bundla i zdecydujemy się na sposób definiowania, przychodzi czas na tworzenie i spisywanie danych. Tworzymy obiekty, a potem trzeba je wczytać. Robimy to za pomocą komendy konsolowej:

bin/console doctrine:mongodb:fixtures:load
bin/console doctrine:mongodb:fixtures:load

Realizacja

W przykładowym projekcie zdecydowałam się na fixtury w formie plików php. Korzystam w wyżej wymienionego DoctrineFixturesBundle. Pierwszą fixturą będzie dla mnie event, organizer i user. Przypomnę, że to są pierwsze wersje dla aktualnych stanów obiektu, będą podlegać aktualizacji razem ze zmianami w encjach.

class LoadData extends AbstractFixture implements OrderedFixtureInterface, FixtureInterface
{
    /**
     * @param ObjectManager $manager
     */
    public function load(ObjectManager $manager)
    {
        /** @var Organizer $organizer */
        $organizer = $this->getReference('organizer_workshops');

        $event = new Event(
            EventTypeProvider::WORKSHOPS,
            new \DateTime('now'),
            new \DateTime('now'),
            $organizer
        );
        $event->setIsCyclic(false);

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

        $this->addReference('event_workshops', $event);
    }

    /**
     * @return int
     */
    public function getOrder()
    {
        return 3;
    }
}

Klasa znajduje się w przestrzeni (EventBundle/UserBundle)\DataFixtures\ORM. Jak już pewnie zauważyłaś/zauważyłeś, implementuje ona dwa interfejsy. FixtureInterface jest wymagany, właśnie po nim zaciągane są wszystkie fixtury do wczytania po wywołaniu komendy load. OrderedFixtureInterface daje nam bardzo fajną funkcjonalność. Poznawała określić, w jakiej kolejności powinny się wczytać fixtury. W przypadku gdy mamy relacje, musimy je również zdefiniować, odnosząc się do innej fixtury. Gdy nie określimy kolejności, może wystąpić błąd, że odniesiemy się do fixtury, która jeszcze nie została wczytana i nie jest w stanie jej wrzucić do bazy, bo nie stworzy wymaganej relacji. Dlatego uważaj na kolejność. Gorąco polecam korzystać z getOrder, by mieć większą kontrolę nad danymi.

Ważną rzeczą jest deklaracja getReference i addReference. Jeśli to Organizer setuje wartość organizer_workshops po to, aby można było go odebrać właśnie w deklaracji eventu powyżej.

Fixtura dla FosUsera

Inny przypadek to definiowanie fixtury dla usera, kiedy używamy FosUserBundle. Aby stworzyć usera, potrzebujemy użyć odpowiedniego serwisu. Wystarczy, że zaimplementujemy ContainerAwareInterface, aby stało się to możliwe. Tak samo będziemy działać zawsze, gdy będziemy chcieli odnieść się do jakiegoś serwisu. Poniżej kod jak to wygląda u mnie.

class LoadData extends AbstractFixture implements OrderedFixtureInterface, FixtureInterface, ContainerAwareInterface
{
    /**
     * @var ContainerInterface
     */
    private $container;

    /**
     * @param ContainerInterface|null $container
     */
    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }

    /**
     * @param ObjectManager $manager
     */
    public function load(ObjectManager $manager)
    {
        $userManager = $this->container->get('fos_user.user_manager');

        $userAdmin = new User();
        $userAdmin->setEmail('test@limecoding.pl');
        $userAdmin->setUsername('admin');
        $userAdmin->setName('admin');
        $userAdmin->setSurname('admin');
        $userAdmin->setPhone('123123123');
        $userAdmin->setEnabled(true);
        $userAdmin->setPlainPassword('test123');
        $userAdmin->setRoles(['ROLE_ADMIN']);

        $userManager->updateUser($userAdmin, true);

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

    /**
     * @return int
     */
    public function getOrder()
    {
        return 1;
    }
}

To chyba na tyle. Daj znać czy zaciekawił Cię ten temat. Jeśli masz dodatkowe pytania w temacie doctrine fixtures, to pisz śmiało. Nie wyczerpałam tematu i na pewno pojawi się jeszcze na tym blogu, więc zachęcam do śledzenia nowych postów.

Podobne posty

Jestem programistką, która lubi mieć ręce pełne roboty. Do życia potrzebuje komputera z internetem i kubka gorącej kawy. Więcej na stronie o mnie.

Comments

  1. Super, fajny art, właśnie po przesiadce z SF 2.8 na 4.x i walce z migracją poprzedniego projektu który był dopisywany ponad pół roku szukałem jakiegoś inicjatora danych w bazie 🙂 dzięki.

ZOSTAW ODPOWIEDŹ

Please enter your comment!
Please enter your name here