Как написать миграцию Doctrine, которая может перераспределять данные в новые таблицы

У меня есть база данных (которая была создана с использованием Propel в приложении Symfony1). Я переоцениваю его в Symfony2 и Doctrine, но я также хочу воспользоваться возможностью для реорганизации базы данных.

Я определил набор Doctrine Entities и запустил doctrine: migrations: diff, который создал мне базовую миграцию для добавления таблиц, столбцов и ограничений и сбросил нагрузку на столбцы.

Однако перед удалением этих столбцов я хочу скопировать данные в некоторые из новых таблиц, а затем связать новые записи в этой таблице с новыми столбцами в первой таблице. Я не думаю, что это можно сделать в чистом SQL (в общем, содержимое одной таблицы распределяется между тремя или четырьмя таблицами).

Это дал мне подсказку и заставил меня найти this ( который я пропустил, потому что я понятия не имел, какая релевантность "контейнеров" может быть для моей проблемы).

Но то, что я не нашел нигде в документации по Symfony или Doctrine, является примером фактического перемещения данных во время миграции, что для меня, по-видимому, является одной из основных целей миграции!

Возможно, что я мог использовать подсказки в этих ссылках выше, но тогда я не уверен, как действовать дальше. У меня нет (и на самом деле не хочу тратить время на создание, хотя я уверен, что могу это сделать) Объекты Doctrine для существующей схемы базы данных: могу ли я затем использовать DQL? Я просто не знаю.

Итак, два вопроса:

  • Может ли кто-нибудь дать мне пример миграции Doctrine, которая перемещает данные между таблицами?

  • В качестве альтернативы, может ли кто-нибудь выяснить, насколько зависим синтаксис DQL от определений сущностей в доктрине? Могу ли я использовать его для указания столбцов, которые не указаны в определениях Entity?

Ответы

Ответ 1

ОК, похоже, я нашел его из нескольких источников (включая this) и проб и ошибок.

Комментарии 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 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'];
      $item = $em->getRepository('PGInventoryBundle:Item')->find($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");
         .
         .
         .

 }