Générer des fixtures Symfony2 à partir de DB?

Est-il possible de générer des lieux à partir d'une base de données existante dans Symfony2/Doctrine? Comment pourrais-je le faire?

Exemple:

j'ai défini 15 entités et mon application symfony2 fonctionne. Maintenant, certaines personnes sont en mesure de rechercher l'application et en l'utilisant, il avait inséré environ 5000 lignes jusqu'à maintenant. Maintenant je veux que les trucs soient insérés comme accessoires, mais je ne veux pas faire ça à la main. Comment puis-je les générer à partir de la base de données?

32
demandé sur A.L 2011-06-12 00:33:45

7 réponses

Il n'y a pas de manière directe dans la Doctrine ou Symfony2, mais écrire un générateur de code pour elle (soit à l'intérieur ou à l'extérieur de sf2) serait trivial. Il suffit de tirer chaque propriété et de générer une ligne de code pour définir chaque propriété, puis de la mettre dans votre méthode de chargement des fixtures. Exemple:

<?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
8
répondu Lusitanian 2012-09-12 14:37:42

comme je comprends votre question, vous avez deux bases de données: la première est déjà en production et remplie avec 5000 lignes, la seconde est une nouvelle base de données que vous voulez utiliser pour le nouveau test et le développement. Est ce que le droit ?

si c'est le cas, je vous suggère de créer dans votre environnement de test deux entity manager: le premier sera celui par défaut, qui sera utilisé dans votre projet (Vos controllers, etc.). Le second sera utilisé pour se connecter à votre base de données de production. Vous trouverez voici comment faire face à de multiples gestionnaire d'entité : http://symfony.com/doc/current/cookbook/doctrine/multiple_entity_managers.html

ensuite, vous devez créer une classe de Fixture qui aura accès à votre conteneur. Il y a un "comment" ici : http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#using-the-container-in-the-fixtures.

en utilisant le conteneur, vous aurez accès aux deux entity manager. Et c'est le 'magic': vous devrez récupérer l'objet de votre base de données de production, et le persister dans le second gestionnaire d'entity, qui les insérera dans votre base de données de test.

je attirer votre attention sur deux points:

  • S'il y a une relation entre les objets, vous devrez prendre soin de ces dépendances: côté propriétaire, côté inversé, ...
  • si vous avez 5000 lignes, prenez soin de la mémoire que votre script utilisera. Une autre solution peut être d'utiliser natif sql pour récupérer toutes les lignes de votre base de données de production et les insérer dans votre base de données de test. Ou un script SQL...

Je n'ai pas de code à vous proposer, mais j'espère que cette idée vous aidera.

4
répondu Julien Fastré 2012-08-30 09:06:14

je suppose que vous voulez utiliser des fixtures (et pas simplement vider la base de données de production ou de mise en scène dans la base de données de développement) parce que a) vos modifications de schéma et les dumps ne fonctionneraient pas si vous mettez à jour votre code ou b) vous ne voulez pas vider la base de données de trou mais voulez seulement étendre quelques fixtures personnalisées. Un exemple que je peux penser est: vous avez 206 pays dans votre base de données de mise en scène et les utilisateurs ajoutent des villes à ces pays; pour garder les appareils Petits, vous avez seulement 5 pays dans votre base de données de développement, cependant vous voulez ajouter les villes que l'Utilisateur a ajouté à ces 5 pays dans la base de données de stadification à la base de données de développement

La seule solution que je vois est d'utiliser le mentionné DoctrineFixturesBundle et plusieurs gestionnaires d'entités.

tout d'abord, vous devez configurer deux connexions de base de données et deux gestionnaires d'entités dans votre 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: ~

comme vous pouvez le voir les deux gestionnaires d'entités mappent L'AcmeDemoBundle (dans ce paquet I mettra le code pour charger les appareils). Si la deuxième base de données n'est pas sur votre machine de développement, vous pouvez simplement jeter le SQL de l'autre machine à la machine de développement. Cela devrait être possible puisque nous parlons de 500 lignes et non sur des millions de lignes.

ce que vous pouvez faire ensuite est de mettre en œuvre un chargeur fixe qui utilise le conteneur de service pour récupérer le deuxième gestionnaire d'entité et D'utiliser la Doctrine pour interroger les données à partir de la deuxième base de données et de le sauver à votre base de données de développement (la default gestionnaire d'entité):

<?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);
    }
}

Ce que j'ai fait dans le loadCountry la méthode était que je charge les objets à partir du staging entity manager, ajouter une référence au pays de l'appareil (celui qui existe déjà dans vos appareils actuels) et le persister en utilisant le default entity manager (votre base de données de développement).

Sources:

3
répondu Florian Eckerstorfer 2012-09-12 22:22:17

les Fixtures de Doctrine sont utiles car elles vous permettent de créer des objets et de les insérer dans la base de données. Ceci est particulièrement utile lorsque vous devez créer des associations ou, disons, encoder un mot de passe en utilisant l'un des encodeurs de mot de passe. Si vous avez déjà les données dans une base de données, vous ne devriez pas vraiment avoir besoin de les sortir de ce format et de les transformer en code PHP, seulement pour avoir ce code PHP insérer les mêmes données dans la base de données. Vous pourriez probablement juste faire un dump SQL et puis insérez - les de nouveau dans votre base de données de cette façon.

L'utilisation d'un montage aurait plus de sens si vous étiez à l'origine de votre projet, mais que vous vouliez utiliser les données de l'utilisateur pour le créer. Si vous aviez dans votre fichier de configuration l'utilisateur par défaut, vous pourriez le lire et insérer l'objet.

0
répondu Isometriks 2012-06-07 20:17:42

AliceBundle peut vous aider à faire cela. En effet, il permet de charger des fixtures avec des fichiers YAML (ou PHP array).

Par exemple, vous pouvez définir vos luminaires avec:

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

ou avec la même structure dans un tableau PHP. C'est beaucoup plus facile que de générer du code PHP.

Il prend également en charge les références:

Nelmio\Entity\User:
    # ...

Nelmio\Entity\Group:
    group1:
        name: Admins
        owner: '@user1'
0
répondu magnetik 2016-05-10 12:46:01

vous pouvez utiliser https://github.com/Webonaute/DoctrineFixturesGeneratorBundle Il ajoute la capacité de générer des fixtures pour une entité unique en utilisant des commandes comme

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

Ou vous pouvez créer un instantané

php app/console doctrine:generate:fixture --snapshot --overwrite
0
répondu Andrew Zhilin 2017-04-05 21:11:19

dans le cookbook doctrine_fixture, vous pouvez voir dans le dernier exemple comment obtenir le conteneur de service dans votre entité.

avec ce conteneur de service, vous pouvez récupérer le service de doctrine, puis le gestionnaire d'entité. Avec le gestionnaire d'entités, vous serez en mesure d'obtenir toutes les données de votre base de données dont vous avez besoin.

espérons que cela vous aidera!

-2
répondu Reuven 2013-01-03 10:09:36