【问题标题】:doctrine2: how to convert a one-to-many to a many-to-many without losing data学说2:如何在不丢失数据的情况下将一对多转换为多对多
【发布时间】:2015-08-05 09:06:22
【问题描述】:

在我的应用程序中,我想在不丢失数据的情况下将一对多转换为多对多:

来自:

/**
 * @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 表中?

【问题讨论】:

  • 你应该使用 DoctrineMigrationsBundle 来更新生产模式
  • 我已经安装了它,但它真的可以将一对多转换为多对多吗?如果有,你知道具体的命令吗?
  • 请阅读文档,关于从实体和现有数据库的差异创建迁移文件
  • 完成,工作正常,谢谢!

标签: mysql symfony doctrine-orm doctrine entity-relationship


【解决方案1】:

好的,我确实找到了一种使用学说迁移的方法。

我首先使用教义:schema:update --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

【讨论】:

  • 这似乎过于复杂,因为您可以只编写一个复制数据的插入语句。对我来说,我只需要将一行添加到生成的迁移中。请参阅下面的答案。
【解决方案2】:

如果您想继续使用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

【讨论】:

    【解决方案3】:

    我通过简单地使用php bin/console make:migration 生成迁移然后使用INSERT 语句扩展迁移来解决这个问题,该语句将旧oneToMany 字段中的所有数据复制到新的manyToMany 表中,然后删除旧列。

    为了坚持问题中的媒体/食谱示例,我将在生成的迁移中的 $this-&gt;addSql('ALTER TABLE media DROP recipeId'); 行之前添加此行:

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

    【讨论】:

    • 当您想要进行简单迁移时非常有用。当然,对于更复杂的答案,接受的答案更灵活。
    猜你喜欢
    • 2023-03-21
    • 2011-08-02
    • 1970-01-01
    • 2020-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-14
    • 1970-01-01
    相关资源
    最近更新 更多