2012-04-27 8 views
12

私はDoctrine2マイグレーション(https://github.com/doctrine/migrations)を行ってきましたが、私は新しいマイグレーションのために質問していますする。

私はライブラリを少し掘り下げましたが、$this->addSql()は実行するSQLのリストを作成するために使用され、後で実行されることがわかりました。

私は何かデータを選択し、行を繰り返し、それに基づいて新しいデータを挿入してから、選択したデータを削除することをやりたかったのです。これは非常に簡単にDBALライブラリに役立ちますが、私は安全にマイグレーションにprotected $connectionを使用できますか?それとも、それは私の$this->addSql() SQLが実行される前にステートメントを実行するので悪いですか?また、これは、私がコードで見たものからdry-run設定を壊すように思えます。誰かがこのタイプの移行に関する経験を持っていましたか?ベストプラクティスはありますか?

次は私がやりたいの移行ですが、私はこれは、教義の移行でサポートされていることを確信していないよ:

public function up(Schema $schema) 
{ 
    // this up() migration is autogenerated, please modify it to your needs 
    $this->abortIf($this->connection->getDatabasePlatform()->getName() != "mysql"); 

    $this->addSql("ALTER TABLE article_enclosures ADD is_scrape TINYINT(1) NOT NULL"); 
    $this->addSql("ALTER TABLE images DROP FOREIGN KEY FK_E01FBE6AA536AAC7"); 

    // now lets take all images with a scrape and convert the scrape to an enclosure 
    // 
    // Select all images where not scrape_id is null (join on article_image_scrape) 
    // for each image: 
    //  insert into article_enclosures 
    //  update image set enclosure_id = new ID 
    //  delete from article_image_scrape where id... 
    // 
    // insert into article_enclosures select article_image_scrapes... 

    $sql = "SELECT i.id img_id, e.* FROM images i JOIN article_image_scrapes e ON i.scrape_id = e.id"; 
    $stmt = $this->connection->prepare($sql); 
    $stmt->execute(); 
    $scrapesToDelete = array(); 
    while ($row = $stmt->fetch()) { 
     $scrapeArticle = $row['article_id']; 
     $scrapeOldId = $row['id']; 
     $scrapeUrl = $row['url']; 
     $scrapeExtension = $row['extension']; 
     $scrapeUrlHash = $row['url_hash']; 
     $imageId = $row['image_id']; 

     $this->connection->insert('article_enclosures', array(
      'url' => $scrapeUrl, 
      'extension' => $scrapeExtension, 
      'url_hash' => $scrapeUrlHash 
     )); 

     $scrapeNewId = $this->connection->lastInsertId(); 

     $this->connection->update('images', array(
      'enclosure_id' => $scrapeNewId, 
      'scrape_id' => null 
     ), array(
      'id' => $imageId 
     )); 

     $scrapesToDelete[] = $scrapeOldId; 
    } 

    foreach ($scrapesToDelete as $id) { 
     $this->connection->delete('article_image_scrapes', array('id' => $id)); 
    } 

    $this->addSql("INSERT INTO article_scrapes (article_id, url, extension, url_hash) " 
      ."SELECT s.id, s.url, s.extension, s.url_hash" 
      ."FROM article_image_scrapes s"); 

    $this->addSql("DROP INDEX IDX_E01FBE6AA536AAC7 ON images"); 
    $this->addSql("ALTER TABLE images DROP scrape_id, CHANGE enclosure_id enclosure_id INT NOT NULL"); 
} 
+0

と私はちょうど必要な 'addSql'で、この1の前と後に、別の移行を行うことを決めている。この例を示しています注文が正しいように呼び出します。 – Matt

+0

試しましたか? – eddy147

+0

私はそうだと思いますが、これは1年前です。私は元の問題はまだ立っていると信じています。つまり、 ' - > addSql()'呼び出しを使用すると、それらは最後に実行されます。そして、「ドライラン」は依然としてあなたの直接の操作を実行します。だから、それはまだハッキーなようです(しかし、私は間違っている可能性があります答えを得ていないと私はこれ以上について多くを覚えていない)。 – Matt

答えて

11

あなたが$connection

このような
$result = $this->connection->fetchAssoc('SELECT id, name FROM table1 WHERE id = 1'); 
$this->abortIf(!$result, 'row with id not found'); 
$this->abortIf($result['name'] != 'jo', 'id 1 is not jo'); 
// etc.. 

を使用することができますデータベースを読み込み、更新/削除を行うために接続を使用しないでください。そうすれば、ドライランオプションが破られることはありません。

例では、2つの移行を行う必要があります。最初は2つのalter tableを実行します。 2番目の方法は、「スクレープ付きの画像を作成し、スクレイプをエンクロージャーに変換する」ルーチンです。複数の移行を使用すると、何か問題が生じた場合に元に戻すことが簡単になります。

8

FYI、最新のドキュメントは、より良い「postUp」方法

http://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html

// ... 
use Symfony\Component\DependencyInjection\ContainerAwareInterface; 
use Symfony\Component\DependencyInjection\ContainerInterface; 

class Version20130326212938 extends AbstractMigration implements ContainerAwareInterface 
{ 

    private $container; 

    public function setContainer(ContainerInterface $container = null) 
    { 
     $this->container = $container; 
    } 

    public function up(Schema $schema) 
    { 
     // ... migration content 
    } 

    public function postUp(Schema $schema) 
    { 
     $em = $this->container->get('doctrine.orm.entity_manager'); 
     // ... update the entities 
    } 
} 
+5

Doctrine Migrations環境で 'EntityManager'を扱う際には注意が必要だと付け加えたいと思います。 EntityManagerは、元の移行で使用されていたものとは異なる現在のエンティティ定義を使用します。私はEntityManagerをマイグレーションに全く使わないでください。 – SteveB

+0

@SteveBはおそらく 'DBAL'レイヤーを使用して解決策になります。 '\ Doctrine \ DBAL \ Connection'という属性の' connection'へのアクセスがあります。 –

+1

@AdrienGええ、私の発言は、 'EntityManager'をショートカットとして使う問題です。それは完璧に見えますが、そうではありません。最終的にエンティティ定義を更新すると、問題を引き起こします。現在のアプリケーションの状態に直接関係しないものを使用すると、まったく問題ありません。 – SteveB

関連する問題