【问题标题】:Generate Symfony2 fixtures from DB?从数据库生成 Symfony2 固定装置?
【发布时间】:2011-09-13 04:29:42
【问题描述】:

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

例子:

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

【问题讨论】:

  • 在谷歌搜索解决方案时遇到了这个问题;我想这里的简短回答是“没有”。很遗憾,因为在 symfony 1.x 中将数据库记录作为固定装置转储是一个非常有用的功能

标签: doctrine-orm fixtures symfony


【解决方案1】:

你可以使用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

【讨论】:

  • 我认为它应该标记为正确答案,因为它简单有效。
【解决方案2】:

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'

【讨论】:

    【解决方案3】:

    在教义_fixture 食谱中,您可以在最后一个示例中看到如何在您的实体中获取服务容器。

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

    希望对您有所帮助!

    【讨论】:

    • 您所说的“doctrine_fixture 食谱”到底指的是什么?你有链接吗?无法弄清楚..然后如果我有实体管理器..我仍然必须自己编写我的夹具文件......但我想要的更像是一个生成这些文件的任务。
    • 我在谈论这个link从现有数据库生成实体,你有这个link当然你必须编写自己的固定装置,我不太明白你想要什么做。我向您解释的是如何在您的功能中使用您现有的数据库。例如,您创建用户固定装置并将它们与您已存储在数据库中的城市相关联。
    • 是的,但这不是我想要的。我知道那些链接。我定义了我的实体并使用我的应用程序在不同的表中插入了大约 500 行。我想将 500 行作为固定装置,因为它们与现有实体匹配。虽然有插件什么的..
    • Stuck 询问如何将 500 行作为固定装置从现有 Symfony 数据库中转储出来,以便稍后再次将它们加载到数据库中。就像 symfony 1.x 能够做到的那样。不幸的是,您的回答没有回答问题。
    【解决方案4】:

    我假设您想使用固定装置(而不仅仅是在开发数据库中转储生产或暂存数据库),因为 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 从第二个数据库中查询数据并将其保存到你的开发数据库(default 实体管理器):

    <?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 实体管理器加载对象,添加对夹具国家/地区的引用(当前夹具中已经存在的那个)并使用@ 持久化它987654330@ entity manager(你的开发数据库)。

    来源:

    【讨论】:

    • 拥有两个数据库并使用夹具加载器将其中一个的内容复制到另一个是可能的,但它不能满足我的需求:我想要夹具格式的数据,而不是放入另一个数据库。我希望能够使用项目本身来创建内容,当我决定时,将其转储到夹具文件中(实现夹具接口的类并正确使用 ORM 将数据加载到 BD 中)。换句话说,我想要一个工具(无论哪种语言),它可以使用 Doctrine 的配置读取 DB,然后编写数据的 php 代码(使用夹具接口和学说)。
    • 现在我想要你需要。如果您使用 DavidBadura/FixturesBundle 可能会更容易,它使用 YAML 作为固定装置的格式。也许我周末有时间开发一些东西。
    【解决方案5】:

    在 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
    

    【讨论】:

    • 在您真正拥有任何相关实体之前,这听起来很简单。另外,在每一个之后进行冲洗是浪费的。
    • 我在下面添加了关于使用 AliceBundle 加载的答案(处理引用)
    【解决方案6】:

    据我了解您的问题,您有两个数据库:第一个已投入生产并填充了 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 脚本...

    我没有任何代码可以推荐给你,但我希望这个想法对你有所帮助。

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

        猜你喜欢
        • 2010-12-20
        • 2014-01-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-07
        • 1970-01-01
        相关资源
        最近更新 更多