从数据库生成 Symfony2 装置?

发布于 2024-11-15 06:41:37 字数 197 浏览 3 评论 0原文

是否可以从 Symfony2/Doctrine 中的现有数据库生成装置?我怎么能这么做呢?

示例:

我定义了 15 个实体,并且我的 symfony2 应用程序正在运行。现在,有些人可以浏览该应用程序,并且通过使用它,到目前为止,它已经插入了大约 5000 行。现在我想要将这些东西作为固定装置插入,但我不想手动执行此操作。我如何从数据库生成它们?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

聚集的泪 2024-11-22 06:41:37

Doctrine 或 Symfony2 中没有直接的方式,但为其编写代码生成器(无论是在 sf2 内部还是外部)将是微不足道的。只需提取每个属性并生成一行代码来设置每个属性,然后将其放入您的装置加载方法中。例子:

<?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

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
メ斷腸人バ 2024-11-22 06:41:37

据我了解你的问题,你有两个数据库:第一个已经投入生产并填充了 5000 行,第二个是你想要用于新测试和开发的新数据库。是这样吗?

如果是,我建议您在测试环境中创建两个实体管理器:第一个是“默认”实体管理器,它将在您的项目(您的控制器等)中使用。第二个将用于连接到您的生产数据库。您将在这里找到如何处理多个实体管理器: http://symfony.com /doc/current/cookbook/doctrine/multiple_entity_managers.html

然后,您应该创建一个可以访问您的容器的 Fixture 类。这里有一个“如何”: http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#using-the-container-in-the-fixtures

使用容器,您将可以访问两个实体管理器。这就是“魔法”:您必须从生产数据库中检索对象,并将它们保存在第二个实体管理器中,该管理器会将它们插入到您的测试数据库中。

我指出两点:

  • 如果对象之间存在关系,则必须注意这些依赖关系:所有者方、倒置方……
  • 如果您有 5000 行,请注意脚本将使用的内存。另一种解决方案可能是使用本机 SQL 从生产数据库中检索所有行并将它们插入到测试数据库中。或者 SQL 脚本...

我没有任何代码可以向您建议,但我希望这个想法对您有所帮助。

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.

绅刃 2024-11-22 06:41:37

我假设您想要使用固定装置(而不仅仅是将生产数据库或临时数据库转储到开发数据库中),因为a)您的架构发生更改,并且如果您更新代码,转储将不起作用,或者b)您不想转储孔数据库,但只想扩展一些自定义夹具。我能想到的一个例子是:您的临时数据库中有 206 个国家/地区,用户将城市添加到这些国家/地区;为了保持灯具较小,您的开发数据库中只有 5 个国家/地区,但是您希望将用户添加到临时数据库中的这 5 个国家/地区的城市添加到开发数据库中

我能想到的唯一解决方案是使用提到的DoctrineFixturesBundle 和多个实体管理器。

首先,您应该在 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: ~

正如您所看到的,两个实体管理器都映射了 AcmeDemoBundle(在此包中,我将放置用于加载固定装置的代码)。如果第二个数据库不在您的开发计算机上,您可以将 SQL 从另一台计算机转储到开发计算机。这应该是可能的,因为我们讨论的是 500 行而不是数百万行。

接下来您可以做的是实现一个夹具加载器,它使用服务容器检索第二个实体管理器,并使用 Doctrine 从第二个数据库查询数据并将其保存到您的开发数据库(默认实体管理器):

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

我在 loadCountry 方法中所做的是从 staging 实体管理器加载对象,添加对固定国家/地区的引用(已经存在的国家/地区)在你当前的装置中)并使用默认实体管理器(您的开发数据库)。

来源:

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:

难得心□动 2024-11-22 06:41:37

你可以使用 https://github.com/Webonaute/DoctrineFixturesGeneratorBundle
它增加了使用类似命令为单个实体生成固定装置的能力

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

或者您可以创建完整快照

php app/console doctrine:generate:fixture --snapshot --overwrite

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
风蛊 2024-11-22 06:41:37

Doctrine Fixtures 非常有用,因为它们允许您创建对象并将它们插入数据库。当您需要创建关联或使用密码编码器之一对密码进行编码时,这尤其有用。如果数据库中已经有数据,那么您实际上不需要将它们从该格式中取出并将其转换为 PHP 代码,只需让该 PHP 代码将相同的数据插入回数据库即可。您可能可以只执行 SQL 转储,然后以这种方式将它们重新插入到数据库中。

如果您正在启动项目但想要使用用户输入来创建项目,那么使用固定装置会更有意义。如果您的配置文件中有默认用户,您可以读取它并插入对象。

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.

万劫不复 2024-11-22 06:41:37

AliceBundle 可以帮助您做到这一点。事实上,它允许使用 YAML(或 PHP 数组)文件加载固定装置。

例如,您可以使用以下方式定义灯具:

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

或者在 PHP 数组中使用相同的结构。这比生成可工作的 PHP 代码要容易得多。

它还支持参考:

Nelmio\Entity\User:
    # ...

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

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'
兲鉂ぱ嘚淚 2024-11-22 06:41:37

在doctrine_fixture 说明书中,您可以在最后一个示例中看到如何在实体中获取服务容器。

使用此服务容器,您可以检索学说服务,然后检索实体管理器。使用实体管理器,您将能够从数据库中获取所需的所有数据。

希望这会对您有所帮助!

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!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文