如何编写一个Doctrine迁移,它可以将数据重新分配到新表中


How do I write a Doctrine migration which can redistribute data into new tables

我有一个数据库(实际上是在Symfony1应用程序中使用Propel创建的)。我正在Symfony2和Doctrine中重新实现它,但我也想借此机会对数据库进行一些重构。

我已经定义了一组Doctrine Entities并运行Doctrine:migrations:diff,这为我创建了一个基本的迁移来添加表、列和约束,并删除列的负载。

但是,在删除这些列之前,我想将数据复制到一些新表中,然后将这些表中的新记录链接到第一个表中的新列。我认为在纯SQL中不可能做到这一点(通常,一个表的内容分布在三个或四个表中)。

这给了我一个提示,并使我找到了这个(我跳过了这个,因为我不知道"容器"可能与我的问题有什么关联)。

但是我在Symfony或Doctrine文档中没有找到一个在迁移中实际移动数据的例子——对我来说,这似乎是迁移的核心目的之一!

我可以使用上面链接中的提示,但我不确定如何进行。我没有(也不想花时间去创建,尽管我确信我可以)现有数据库模式的原则实体:那么我可以使用DQL吗?我真的不知道。

两个问题:

  1. 有人能给我一个例子,一个学说迁移移动表之间的数据?

  2. 或者,谁能澄清DQL的语法是如何依赖于实体在原则的定义?我可以用它来指定列不是在实体定义?

好的,我似乎已经找到了它,从许多来源(包括这个)和试验和错误。

Cerad的评论有一点帮助,但主要是我通过使用DBAL层读取数据(我可以通过$this->connection获得)和ORM来保存新数据(这需要EntityManager,所以我确实必须使用容器的技巧)。

我把所有的代码放在postUp()中,包括生成的从表中删除列的代码。

示例代码:

use Symfony'Component'DependencyInjection'ContainerAwareInterface;
use Symfony'Component'DependencyInjection'ContainerInterface;
use PG'InventoryBundle'Entity'Item;
use PG'InventoryBundle'Entity'Address;
         .
         .
         .
/**
 * Auto-generated Migration: Please modify to your needs!
 */
class Version20140519211228 extends AbstractMigration implements ContainerAwareInterface
{
  private $container;
  public function setContainer(ContainerInterface $container = null)
  {
    $this->container = $container;
  }
  public function up(Schema $schema)
  {
         .
         .
         .
  }
}
public function postUp(Schema $schema)
{
    $em = $this->container->get('doctrine.orm.entity_manager');
    // ... update the entities
    $query = "SELECT * FROM item";
    $stmt = $this->connection->prepare($query);
    $stmt->execute();
    // We can't use Doctrine's ORM to fetch the item, because it has a load of extra fields
    // that aren't in the entity definition.
    while ($row = $stmt->fetch()) {
      // But we will also get the entity, so that we can put addresses in it.
      $id = $row['id'];
      // And create new objects
      $stock = new Stock();
         .
         .
         .
      $stock->setAssetNo($row['asset_no']);
      $stock->setItemId($row['id']);
      $em->persist($stock);
      $em->flush();
    }
    // Now we can drop fields we don't need. 
    $this->connection->executeQuery("ALTER TABLE item DROP container_id");
    $this->connection->executeQuery("ALTER TABLE item DROP location_id");
         .
         .
         .
 }