2012-04-27 2 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

는 "postUp"방법도 더이 예제를 보여 addSql' 순서가 올 바르도록 호출합니다. – Matt

+0

시도해 보셨습니까? 나에게 잘 어울리는데 – eddy147

+0

나는 그렇게 생각한다. 그러나 이것은 1 년 전이었다. 나는 원래의 이슈가 여전히 유효하다고 생각한다. 즉,'-> addSql()'호출을 사용하면 마지막 호출이 실행됩니다. 그리고'드라이 런 (dry-run) '은 여전히 ​​당신의 직접 조작을 실행할 것입니다. 그래서 그것은 여전히 ​​해킹 된 것처럼 보입니다 (그러나 나는 틀릴 수도 있고 대답을 얻지도 못했고 더 이상 이것에 대해 많이 기억하지 않습니다). – Matt

답변

11

당신이

$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.. 

처럼 $connection를 사용 할 수 있습니다 데이터베이스 만 읽어야하고 업데이트/삭제를 위해 연결을 사용하지 않아야합니다. 그러면 마른 실행 옵션이 손상되지 않습니다.

예제에서 두 가지 마이그레이션을 수행해야합니다. 첫 번째는 두 개의 alter table을 수행합니다. 두 번째는 "긁힌 자국이있는 이미지"를 수행하고 긁힌자를 외장으로 변환합니다. 실수로 문제가 발생하면 여러 마이그레이션을 사용하면 쉽게 되돌릴 수 있습니다.

8

는 참고로, 최신 문서는 내가 전에 '필요한이 일 후 별도의 마이그레이션을 단지를하기로 결정

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

관련 문제