web-dev-qa-db-ja.com

DBからSymfony2フィクスチャを生成しますか?

Symfony2/Doctrineの既存のDBからフィクスチャを生成することは可能ですか?どうすればそれができますか?

例:

15個のエンティティを定義し、symfony2アプリケーションが機能しています。今では何人かの人々がアプリケーションを閲覧することができ、それを使用することによってこれまで約5000行を挿入していました。今はフィクスチャーとして挿入したいのですが、手作業ではやりたくありません。 DBからそれらを生成するにはどうすればよいですか?

32
Stuck

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
8
Lusitanian

あなたの質問を理解しているように、2つのデータベースがあります。1つはすでに本番環境にあり、5000行で埋められています。もう1つは、新しいテストと開発に使用する新しいデータベースです。そうですか?

そうである場合は、テスト環境で2つのエンティティマネージャーを作成することをお勧めします。最初のエンティティマネージャーは、プロジェクト(コントローラーなど)で使用される「デフォルト」のものになります。 2つ目は、本番データベースへの接続に使用されます。複数のエンティティマネージャーを処理する方法はここにあります: 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

コンテナを使用すると、両方のエンティティマネージャにアクセスできます。そしてこれが「魔法」です。本番データベースからオブジェクトを取得し、それらを2番目のエンティティマネージャーに永続化して、テストデータベースに挿入する必要があります。

私はあなたの注意を2つの点に向けます:

  • オブジェクト間に関係がある場合は、所有者側、反転側、...などの依存関係に注意する必要があります。
  • 5000行ある場合は、スクリプトが使用するメモリに注意してください。別の解決策は、ネイティブSQLを使用して、本番データベースからすべての行を取得し、それらをテストデータベースに挿入することです。またはSQLスクリプト...

提案するコードはありませんが、このアイデアがお役に立てば幸いです。

4
Julien Fastré

A)スキーマが変更され、コードを更新するとダンプが機能しない、またはb)ダンプしたくないため、フィクスチャを使用する(本番データベースまたはステージングデータベースを開発データベースにダンプするだけではない)と仮定します。穴データベースですが、一部のカスタムフィクスチャのみを拡張したいです。私が考えることができる例は次のとおりです。ステージングデータベースに206か国があり、ユーザーはそれらの国に都市を追加します。フィクスチャを小さく保つために、開発データベースには5か国しかありませんが、ステージングデータベースでユーザーがこれら5か国に追加した都市を開発データベースに追加する必要があります。

私が考えることができる唯一の解決策は、前述のDoctrineFixturesBundleと複数のエンティティマネージャーを使用することです。

まず、config.ymlで2つのデータベース接続と2つのエンティティマネージャーを構成する必要があります

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をマップします(このバンドルには、フィクスチャをロードするためのコードを配置します)。 2番目のデータベースが開発マシン上にない場合は、SQLを他のマシンから開発マシンにダンプするだけで済みます。数百万行ではなく500行について話しているので、それは可能であるはずです。

次にできることは、サービスコンテナを使用して2番目のエンティティマネージャを取得し、Doctrineを使用して2番目のデータベースからデータをクエリし、開発データベースに保存するフィクスチャローダーを実装することです( 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エンティティマネージャーからオブジェクトをロードし、フィクスチャーの国(現在のフィクスチャーに既に存在する国)への参照を追加して、それを永続化することでした。 defaultエンティティマネージャー(開発データベース)を使用します。

出典:

使用できます 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
1
Andrew Zhilin

AliceBundle は、これを行うのに役立ちます。実際、YAML(またはPHP array)ファイルでフィクスチャをロードできます。

たとえば、次の方法で器具を定義できます。

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

または、PHP配列に同じ構造を使用します。動作するPHPコードを生成するよりもはるかに簡単です。

また、参照もサポートしています。

Nelmio\Entity\User:
    # ...

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

Doctrineフィクスチャは、オブジェクトを作成してデータベースに挿入できるので便利です。これは、関連付けを作成する必要がある場合や、パスワードエンコーダの1つを使用してパスワードをエンコードする必要がある場合に特に便利です。 。データベースにすでにデータがある場合は、実際にその形式からデータを取り出してPHPコードに変換する必要はなく、PHPコードは、同じデータをデータベースに挿入し直します。おそらく、SQLダンプを実行してから、その方法でデータベースに再挿入することができます。

プロジェクトを開始しているが、ユーザー入力を使用してプロジェクトを作成したい場合は、フィクスチャを使用する方が理にかなっています。設定ファイルにデフォルトのユーザーが含まれている場合は、それを読み取ってオブジェクトを挿入できます。

0
Isometriks