我正在进行一个基于Symfony 3.4+条令的项目。生产数据库已经变得相当大,我希望能够将一些数据/实体复制到第二个数据库中,该数据库可以用作对数据运行测试、评估等的沙箱。
将第二个数据库连接添加到项目中没有问题。第二步是查询数据库模式/表结构形式Doctrine,为第二个数据库中的一些实体重新创建完全相同的表。
现在,我想像往常一样使用实体管理器从生产数据库中查询实体,并将它们持久化到第二个数据库中。由于第二个DB只保存一些数据/实体/表,我不能在这里使用第二个实体管理器,而是必须手动插入数据。
我正在寻找类似的东西:
// Load entity from production DB via entity manager
$repo = $this->em->getRepository(SomeEntity::class);
$entity = $repo->findOneById('xy12');
// Pseudocode(!): Get SQL code to save the entity
$saveQuery = $repo->getSaveQueryForEntity(entity); <<< HOW TO DO THIS?
$saveSql = saveQuery->getSql();
// Run SQL on sandbox connection
$sandboxConnection = $doctrine->getConnection('sandbox');
$sandboxConnection->executeQuery($saveSql);
当然,我可以完全手动创建INSERT
查询。然而,这将是相当麻烦和容易出错的。另一方面,创建已经构建在Doctrine中的SQL代码,而我所需要的只是一种访问/获取此代码的方法,以便在不同的连接上运行它?
还是这种方法完全错误,并且有更好的方法将实体从一个DB转移到另一个DB?
编辑:
转储完整的数据库并将其导入沙箱数据库不是一种选择。数据库保存了许多注册用户的数据,每个用户都可以决定是否以及何时将一些数据传输到沙箱。因为用户123想在实体a和B上运行一些测试,所以将一个包含所有用户数据的几GB大数据库复制到沙盒中不是很有效,是吗?
我不想在这里描述项目的完整内部逻辑,因为这对问题没有真正的帮助。因此,问题是如何通过从条令中获取SQL来将单个实体复制/移动到另一个数据库:-(
你说:
由于第二个DB只保存一些数据/实体/表,我不能在这里使用第二个实体管理器,而是必须手动插入数据。
但您仍然可以声明两个不同的实体管理器,它们都映射同一个实体,但具有不同的映射选项(例如,可能您不想用另一个实体管理器映射所有字段(。
您需要有两个绑定到同一实体的不同映射,因此最好使用单独的YML文件(而不是注释(。你可以有这样的东西:
config/orm/default/User.orm.yml // User mapping for the default EM
config/orm/other/User.orm.yml // User mapping for the other EM
此外,用默认实体管理器加载实体并用另一个持久化实体也不会按预期工作。您将不得不使用merge()
而不是persist()
,因为实体将由默认的实体管理器管理:
$user = $defaultEntityManager->getRepository(User::class)->find(1);
$otherEntityManager->merge($user);
$otherEntityManager->flush();
考虑到在数据库级别使用mysqldump绝对不合适,我可能会为项目启用两个实体管理器,并为它们维护完全相同的数据模式。它提供了在需要时持久化类似对象的机会。当您的用户选择要在网页上复制的实体时,我们可以将这些id传递给处理程序,以从主实体管理器中获取实体,并将其同步到沙盒管理器中。与从Doctrine中获取insert SQL查询相比,它应该非常简单,也不那么麻烦。这里有一个简单的例子给你一个起点。
<?php
declare(strict_types=1);
namespace AppSync;
use DoctrineORMEntityManagerInterface;
class SandboxDbSyncHandler
{
/** @var EntityManagerInterface */
private EntityManagerInterface $em;
/** @var EntityManagerInterface */
private EntityManagerInterface $sandboxEm;
public function __construct(EntityManagerInterface $em, EntityManagerInterface $sandboxEm)
{
$this->em = $em;
$this->sandboxEm = $sandboxEm;
}
public function sync(string $class, array $ids): void
{
$repo = $this->em->getRepository($class);
$sandBoxRepo = $this->sandboxEm->getRepository($class);
$entities = $repo->findBy(['id' => $ids]);
foreach ($entities as $entity) {
if (!$entity instanceof UpdatableFromEntity) {
continue;
}
$sandBoxEntity = $sandBoxRepo->find($entity->getId());
if (!$sandBoxEntity) {
$sandBoxEntity = $class()::createFromEntity();
}
$sandBoxEntity->updateFromEntity($entity);
$this->sandboxEm->persist($entity);
}
$this->sandboxEm->flush();
}
}
<?php
declare(strict_types=1);
namespace AppEntity;
interface UpdatableFromEntity
{
public static function createFromEntity($entity);
public function updateFromEntity($entity): void;
}
<?php
declare(strict_types=1);
namespace AppEntity;
class SomeEntity implements UpdatableFromEntity
{
private string $id;
private ?string $name;
public function __construct(string $id)
{
$this->id = $id;
}
public function getId(): string
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function updateFromEntity($entity): void
{
if (!$entity instanceof SomeEntity::class) {
throw new Exception('Cannot update an entity from the entity of a different type');
}
$this->name = $entity->getName();
}
public static function createFromEntity($entity)
{
if (!$entity instanceof SomeEntity::class) {
throw new Exception('Cannot create an entity from the entity of a different type');
}
return new static($entity->getId());
}
}