doctrine2:如何在不丢失数据的情况下将一对多转换为多对多
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.
我该如何解决这个问题?即,将食谱 table 的前一个 mysql mediaId 列与食谱 ID ?
添加到新的 media_recipe table
好的,我确实找到了一种使用学说迁移的方法。
我首先使用 doctrine:schema:update --force 确保我的更改设置正确,检查分析器,然后恢复到上一个数据库。
所以使用学说迁移包:
doctrine:migrations:diff
创建一个新迁移 class
编辑 class 以满足您的需要。我的是:
<?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
如果您想继续使用 orm:schema-tool:update --force
而不是采用 doctrine:migrations:migrate
,则可以使用两步过程将关系从多对一迁移到多对多。
- 创建一个
many-to-many-step1
分支
- 在旧的@ManyToOne/@JoinColumn/@OneToMany 属性和访问器旁边添加新的@ManyToMany/@JoinTable 属性和访问器
- 部署代码并运行
orm:schema-tool:update --force
- 使用
INSERT INTO ... SELECT
语句 将旧连接列内容填充到新连接 table
- 创建一个
many-to-many-step2
分支
- 更改代码以使用新的多对多关系和访问器
- 删除旧的多对一关系的所有工件
- 部署代码并运行
orm:schema-tool:update --force
我已经解决了这个问题,方法是简单地使用 php bin/console make:migration
生成迁移,然后使用 INSERT 语句扩展迁移,该语句将所有数据从旧的 oneToMany 字段复制到新的 manyToMany table 中,然后删除旧专栏。
为了坚持问题中的 media/recipes 示例,我将在生成的迁移中的 $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\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.
我该如何解决这个问题?即,将食谱 table 的前一个 mysql mediaId 列与食谱 ID ?
添加到新的 media_recipe table好的,我确实找到了一种使用学说迁移的方法。
我首先使用 doctrine:schema:update --force 确保我的更改设置正确,检查分析器,然后恢复到上一个数据库。
所以使用学说迁移包:
doctrine:migrations:diff
创建一个新迁移 class
编辑 class 以满足您的需要。我的是:
<?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
如果您想继续使用 orm:schema-tool:update --force
而不是采用 doctrine:migrations:migrate
,则可以使用两步过程将关系从多对一迁移到多对多。
- 创建一个
many-to-many-step1
分支- 在旧的@ManyToOne/@JoinColumn/@OneToMany 属性和访问器旁边添加新的@ManyToMany/@JoinTable 属性和访问器
- 部署代码并运行
orm:schema-tool:update --force
- 使用
INSERT INTO ... SELECT
语句 将旧连接列内容填充到新连接 table
- 创建一个
many-to-many-step2
分支- 更改代码以使用新的多对多关系和访问器
- 删除旧的多对一关系的所有工件
- 部署代码并运行
orm:schema-tool:update --force
我已经解决了这个问题,方法是简单地使用 php bin/console make:migration
生成迁移,然后使用 INSERT 语句扩展迁移,该语句将所有数据从旧的 oneToMany 字段复制到新的 manyToMany table 中,然后删除旧专栏。
为了坚持问题中的 media/recipes 示例,我将在生成的迁移中的 $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');