簡體   English   中英

學說2:如何在不丟失數據的情況下將一對多轉換為多對多

[英]doctrine2: how to convert a one-to-many to a many-to-many without losing data

在我的應用程序中,我想在不丟失數據的情況下將一對多轉換為多對多:

從:

/**
 * @ORM\ManyToOne(targetEntity="\AppBundle\Entity\FoodAnalytics\Recipe", inversedBy="medias")
 * @ORM\JoinColumn(name="recipeId", referencedColumnName="id", onDelete="CASCADE")
 */
protected $recipe;

至:

/**
 * @ORM\ManyToMany(targetEntity="\AppBundle\Entity\FoodAnalytics\Recipe", inversedBy="medias")
 * @ORM\JoinTable(
 *     name="media_recipes",
 *     joinColumns={@ORM\JoinColumn(name="mediaId", referencedColumnName="id", onDelete="CASCADE")},
 *     inverseJoinColumns={@ORM\JoinColumn(name="recipeId", referencedColumnName="id", onDelete="CASCADE")}
 * )
 */
protected $recipes;

當我轉儲我的學說 shema 更新時,它說它將刪除數據,而不是我想要的:

CREATE TABLE media_recipes (mediaId INT UNSIGNED NOT NULL, recipeId INT UNSIGNED NOT NULL, INDEX IDX_C2BE64FC27D9F5AC (mediaId), INDEX IDX_C2BE64FC6DCBA54 (recipeId), PRIMARY KEY(mediaId, recipeId)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
ALTER TABLE media_recipes ADD CONSTRAINT FK_C2BE64FC27D9F5AC FOREIGN KEY (mediaId) REFERENCES media (id) ON DELETE CASCADE;
ALTER TABLE media_recipes ADD CONSTRAINT FK_C2BE64FC6DCBA54 FOREIGN KEY (recipeId) REFERENCES Recipe (id) ON DELETE CASCADE;
ALTER TABLE media DROP FOREIGN KEY FK_6A2CA10C6DCBA54;
DROP INDEX IDX_6A2CA10C6DCBA54 ON media;
ALTER TABLE media DROP recipeId;

Process finished with exit code 0 at 13:04:46.
Execution time: 5 003 ms.

我該如何解決這個問題? 即是否將配方表的先前 mysql mediaId 列與配方 ID 一起添加到新的 media_recipe 表中?

好的,我確實找到了一種使用學說遷移的方法。

我首先使用一個學說:模式:更新 --force 確保我的更改設置正確,檢查分析器,然后恢復到最后一個數據庫。

所以使用學說遷移捆綁:

doctrine:migrations:diff

創建一個新的遷移類

編輯類以滿足您的需要。 我的是:

<?php

namespace Application\Migrations;

use AppBundle\Entity\Core\Media;
use AppBundle\Entity\FoodAnalytics\Recipe;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Auto-generated Migration: Please modify to your needs!
 */
class Version20150525154902 extends AbstractMigration implements ContainerAwareInterface
{

    private $customSQL = array();

    /** @var Container */
    private $container;


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

    /**
     * @param Schema $schema
     */
    public function preUp(Schema $schema)
    {
        $query = "SELECT id as mediaId, recipeId FROM `media` WHERE recipeId IS NOT NULL";
        $data = $this->connection->prepare($query);
        $data->execute();
        foreach ($data as $row)
        {
            $mediaId = $row['mediaId'];
            $recipeId = $row['recipeId'];
            $this->customSQL[] = "($mediaId, $recipeId)";
        }

    }

    /**
     * @param Schema $schema
     */
    public function up(Schema $schema)
    {
        // this up() migration is auto-generated, please modify it to your needs
        $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');

        $this->addSql('CREATE TABLE media_recipes (mediaId INT UNSIGNED NOT NULL, recipeId INT UNSIGNED NOT NULL, INDEX IDX_C2BE64FC27D9F5AC (mediaId), INDEX IDX_C2BE64FC6DCBA54 (recipeId), PRIMARY KEY(mediaId, recipeId)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
        $this->addSql('ALTER TABLE media_recipes ADD CONSTRAINT FK_C2BE64FC27D9F5AC FOREIGN KEY (mediaId) REFERENCES media (id) ON DELETE CASCADE');
        $this->addSql('ALTER TABLE media_recipes ADD CONSTRAINT FK_C2BE64FC6DCBA54 FOREIGN KEY (recipeId) REFERENCES Recipe (id) ON DELETE CASCADE');
        $this->addSql('ALTER TABLE media DROP FOREIGN KEY FK_6A2CA10C6DCBA54');
        $this->addSql('DROP INDEX IDX_6A2CA10C6DCBA54 ON media');
        $this->addSql('ALTER TABLE media DROP recipeId');
        $this->addSql('ALTER TABLE recipe ADD versionDetails VARCHAR(200) DEFAULT NULL');

    }

    public function postUp(Schema $schema)
    {
        $SQL = 'INSERT INTO media_recipes (mediaId, recipeId) VALUES ' . implode(', ', $this->customSQL);

        $this->connection->executeQuery($SQL);
    }



    /**
     * @param Schema $schema
     */
    public function down(Schema $schema)
    {
        // this down() migration is auto-generated, please modify it to your needs
        $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');

        $this->addSql('DROP TABLE media_recipes');
        $this->addSql('ALTER TABLE media ADD recipeId INT UNSIGNED DEFAULT NULL');
        $this->addSql('ALTER TABLE media ADD CONSTRAINT FK_6A2CA10C6DCBA54 FOREIGN KEY (recipeId) REFERENCES recipe (id) ON DELETE CASCADE');
        $this->addSql('CREATE INDEX IDX_6A2CA10C6DCBA54 ON media (recipeId)');
    }
}

然后執行更新:

doctrine:migrations:migrate

並確認y

我通過簡單地使用php bin/console make:migration生成遷移來解決這個問題,然后使用 INSERT 語句擴展遷移,該語句在刪除舊列之前將舊 oneToMany 字段中的所有數據復制到新的 manyToMany 表中。

為了堅持問題中的媒體/食譜示例,我將在$this->addSql('ALTER TABLE media DROP recipeId');之前添加這一行。 生成遷移中的行:

$this->addSql('INSERT INTO media_recipes (mediaId, recipeId) SELECT id, recipeId FROM media WHERE recipeId IS NOT NULL');

如果您想繼續使用orm:schema-tool:update --force而不是采用doctrine:migrations:migrate ,那么您可以使用兩步過程將關系從多對一遷移到多對多。

  1. 創建many-to-many-step1分支
    • 在舊的 @ManyToOne/@JoinColumn/@OneToMany 屬性和訪問器旁邊添加新的 @ManyToMany/@JoinTable 屬性和訪問器
    • 部署代碼並運行orm:schema-tool:update --force
    • 使用INSERT INTO ... SELECT語句用舊的連接列內容填充新連接表
  2. 創建many-to-many-step2分支
    • 更改代碼以使用新的多對多關系和訪問器
    • 刪除舊的多對一關系的所有工件
    • 部署代碼並運行orm:schema-tool:update --force

一種簡單的方法是使用以下命令刪除該列的唯一約束:

ALTER TABLE relation_table DROP INDEX UK_constraint_id;

如果您安裝了 MySql Workbench,那就更容易了,因為您不需要發現約束 id。 只需選擇您的表 -> 更改表並取消選中該列的 UQ 復選框,然后單擊“應用”。

在此處輸入圖像描述

如果您沒有安裝 MySql Workbench,您可以執行以下查詢以找出約束 id:

USE INFORMATION_SCHEMA;
SELECT TABLE_NAME,
       COLUMN_NAME,
       CONSTRAINT_NAME,
       REFERENCED_TABLE_NAME,
       REFERENCED_COLUMN_NAME
FROM KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = "<schema_name>" 
      AND TABLE_NAME = "<table_name>";

並且只需使用上面提到的 ALTER TABLE。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM