32

Is it possible to generate fixtures from an existing DB in Symfony2/Doctrine? How could I do that?

Example:

I have defined 15 entities and my symfony2 application is working. Now some people are able to browse to the application and by using it it had inserted about 5000 rows until now. Now I want the stuff inserted as fixtures, but I don’t want to do this by hand. How can I generate them from the DB?

A.L
  • 10,259
  • 10
  • 67
  • 98
Stuck
  • 11,225
  • 11
  • 59
  • 104
  • 3
    happened across this while googling for a solution; I guess the short answer here is "there isn't one". a shame, since dumping database records as fixtures was such a useful feature in symfony 1.x – Darragh Enright Jan 25 '12 at 13:44

7 Answers7

8

There's no direct manner within Doctrine or Symfony2, but writing a code generator for it (either within or outside of sf2) would be trivial. Just pull each property and generate a line of code to set each property, then put it in your fixture loading method. Example:

<?php
$i = 0;
$entities = $em->getRepository('MyApp:Entity')->findAll();
foreach($entities as $entity)
{
   $code .= "$entity_{$i} = new MyApp\Entity();\n";
   $code .= "$entity_{$i}->setMyProperty('" . addslashes($entity->getMyProperty()); . "'); \n");
   $code .= "$manager->persist($entity_{$i}); \n $manager->flush();";
   ++$i;
}
// store code somewhere with file_put_contents
Lusitanian
  • 11,012
  • 1
  • 41
  • 38
  • 9
    It sounds trivial until you actually have any related entity. Plus, doing a flush after every single one is wasteful. – Jessica Oct 18 '13 at 20:24
  • I've added my answer about loading with AliceBundle below (that handle references) – magnetik May 10 '16 at 12:46
4

As I understand your question, you have two databases: the first is already in production and filled with 5000 rows, the second one is a new database you want to use for new test and development. Is that right ?

If it is, I suggest you to create in you test environment two entity manager: the first will be the 'default' one, which will be used in your project (your controllers, etc.). The second one will be used to connect to your production database. You will find here how to deal with multiple entity manager : http://symfony.com/doc/current/cookbook/doctrine/multiple_entity_managers.html

Then, you should create a Fixture class which will have access to your container. There is an "how to" here : http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#using-the-container-in-the-fixtures.

Using the container, you will have access to both entity manager. And this is the 'magic': you will have to retrieve the object from your production database, and persist them in the second entity manager, which will insert them in your test database.

I point your attention to two points:

  • If there are relationship between object, you will have to take care to those dependencies: owner side, inversed side, ...
  • If you have 5000 rows, take care on the memory your script will use. Another solution may be use native sql to retrieve all the rows from your production database and insert them in your test database. Or a SQL script...

I do not have any code to suggest to you, but I hope this idea will help you.

Julien Fastré
  • 1,008
  • 13
  • 20
3

I assume that you want to use fixtures (and not just dump the production or staging database in the development database) because a) your schema changes and the dumps would not work if you update your code or b) you don't want to dump the hole database but only want to extend some custom fixtures. An example I can think of is: you have 206 countries in your staging database and users add cities to those countries; to keep the fixtures small you only have 5 countries in your development database, however you want to add the cities that the user added to those 5 countries in the staging database to the development database

The only solution I can think of is to use the mentioned DoctrineFixturesBundle and multiple entity managers.

First of all you should configure two database connections and two entity managers in your config.yml

doctrine:
    dbal:
        default_connection: default
        connections:
            default:
                driver:   %database_driver%
                host:     %database_host%
                port:     %database_port%
                dbname:   %database_name%
                user:     %database_user%
                password: %database_password%
                charset:  UTF8
            staging:
                ...

    orm:
        auto_generate_proxy_classes: %kernel.debug%
        default_entity_manager:   default
        entity_managers:
            default:
                connection:       default
                mappings:
                    AcmeDemoBundle: ~
            staging:
                connection:       staging
                mappings:
                    AcmeDemoBundle: ~

As you can see both entity managers map the AcmeDemoBundle (in this bundle I will put the code to load the fixtures). If the second database is not on your development machine, you could just dump the SQL from the other machine to the development machine. That should be possible since we are talking about 500 rows and not about millions of rows.

What you can do next is to implement a fixture loader that uses the service container to retrieve the second entity manager and use Doctrine to query the data from the second database and save it to your development database (the default entity manager):

<?php

namespace Acme\DemoBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Acme\DemoBundle\Entity\City;
use Acme\DemoBundle\Entity\Country;

class LoadData implements FixtureInterface, ContainerAwareInterface
{
    private $container;
    private $stagingManager;

    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
        $this->stagingManager = $this->container->get('doctrine')->getManager('staging');
    }

    public function load(ObjectManager $manager)
    {
        $this->loadCountry($manager, 'Austria');
        $this->loadCountry($manager, 'Germany');
        $this->loadCountry($manager, 'France');
        $this->loadCountry($manager, 'Spain');
        $this->loadCountry($manager, 'Great Britain');
        $manager->flush();
    }

    protected function loadCountry(ObjectManager $manager, $countryName)
    {
        $country = new Country($countryName);
        $cities = $this->stagingManager->createQueryBuilder()
            ->select('c')
            ->from('AcmeDemoBundle:City', 'c')
            ->leftJoin('c.country', 'co')
            ->where('co.name = :country')
            ->setParameter('country', $countryName)
            ->getQuery()
            ->getResult();
        foreach ($cities as $city) {
            $city->setCountry($country);
            $manager->persist($city);
        }
        $manager->persist($country);
    }
}

What I did in the loadCountry method was that I load the objects from the staging entity manager, add a reference to the fixture country (the one that already exists in your current fixtures) and persist it using the default entity manager (your development database).

Sources:

Florian Eckerstorfer
  • 1,526
  • 1
  • 14
  • 21
  • 2
    Having two DB and using the fixture loader to copy the content of one to the other is kind of possible but it doesn't answer my needs: I want to have the data in fixtures format, not into an other DB. And I want to be able to use the project itself to create the content and when I decide to, dump it into fixtures files (classes implementing fixtures interface and correctly using the ORM to load the data into the BD). In other words, I want a tool (whichever language) that can read the DB using Doctrine's configuration and then writes php code (using fixture interface and doctrine) of the data. – qwertzguy Sep 14 '12 at 09:26
  • Now I get want you need. Maybe it is easier if you would use [DavidBadura/FixturesBundle](https://github.com/DavidBadura/FixturesBundle) which uses YAML as format for fixtures. Maybe I have time on the weekend to develop something. – Florian Eckerstorfer Sep 14 '12 at 10:24
3

you could use https://github.com/Webonaute/DoctrineFixturesGeneratorBundle It add ability to generate fixtures for single entity using commands like

$ php bin/console doctrine:generate:fixture --entity=Blog:BlogPost --ids="12 534 124" --name="bug43" --order="1"

Or you can create full snapshot

php app/console doctrine:generate:fixture --snapshot --overwrite
Andrew Zhilin
  • 1,654
  • 16
  • 11
0

The Doctrine Fixtures are useful because they allow you to create objects and insert them into the database. This is especially useful when you need to create associations or say, encode a password using one of the password encoders. If you already have the data in a database, you shouldn't really need to bring them out of that format and turn it into PHP code, only to have that PHP code insert the same data back into the database. You could probably just do an SQL dump and then re-insert them into your database again that way.

Using a fixture would make more sense if you were initiating your project but wanted to use user input to create it. If you had in your config file the default user, you could read that and insert the object.

0

The AliceBundle can help you doing this. Indeed it allows to load fixtures with YAML (or PHP array) files.

For instance you can define your fixtures with:

Nelmio\Entity\Group:
    group1:
        name: Admins
        owner: '@user1->id'

Or with the same structure in a PHP array. It's WAY easier than generating working PHP code.

It also supports references:

Nelmio\Entity\User:
    # ...

Nelmio\Entity\Group:
    group1:
        name: Admins
        owner: '@user1'
magnetik
  • 4,351
  • 41
  • 58
-3

In the doctrine_fixture cookbook, you can see in the last example how to get the service container in your entity.

With this service container, you can retrieve the doctrine service, then the entity manager. With the entity manager, you will be able to get all the data from your database you need.

Hope this will help you!

Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
Reuven
  • 3,336
  • 2
  • 23
  • 29
  • What exactly are you refering to by saying "doctrine_fixture cookbook" ? Do you have a link? wasnt able to figure it out.. and then if I have the entity manager.. I still have to write my fixture files on my own... but what I want is more like a task that generates these files. – Stuck Jun 15 '11 at 20:32
  • I'm talking about this [link](http://symfony.com/doc/current/cookbook/doctrine/doctrine_fixtures.html#using-the-container-in-the-fixtures) For generating Entities from an existing database, you have this [link](http://symfony.com/doc/current/cookbook/doctrine/reverse_engineering.html) Sure you have to write your own fixtures, I don't really understand what you want to do. What I explained you is how to use your existing data base in your feature. For instance, you creates user fixtures and associate them with Cities you already store in your data base. – Reuven Jun 16 '11 at 11:17
  • 1
    yes, but it is not what I'm asking for. I know those links. I have my entities defined and using my app I inserted about 500 rows in different tables. And I want to get the 500 rows as fixtures, because they match up existing entities. Though that there is a plugin or something.. – Stuck Jun 16 '11 at 12:31
  • Stuck is asking how to dump 500 rows as fixtures out of an existing Symfony database to load them into the database again later. Just like symfony 1.x was able to do. And your answer unfortunately didn't answer the question. – flu May 03 '12 at 09:28