嗨,我有这样的结构:Home N <-> N个群组
Home.orm.yml
manyToMany:
groups:
targetEntity: Domain\Entity\Group
inversedBy: homes
joinTable:
name: homes_groups
joinColumns:
home_id:
referencedColumnName: id
onDelete: CASCADE
inverseJoinColumns:
group_id:
referencedColumnName: id
onDelete: CASCADE
Home.php
protected $groups;
protected $name;
public function __construct($name, Group $group) {
$this->name = $name;
$this->groups = new ArrayCollection();
$this->addGroup($group);
}
public function getGroups() {
return $this->groups();
}
public function addGroup(Group $group) {
if (!$this->getGroups()->contains($group)) {
$this->getGroups()->add($group);
$group->addUser($this);
}
}
Group.orm.yml
manyToMany:
homes:
targetEntity: Domain\Entity\Home
orderBy: { 'name': 'ASC' }
mappedBy: groups
Group.php
protected $homes;
public function __construct() {
$this->homes = new ArrayCollection();
}
public function getHomes() {
return $this->homes();
}
public function addHome(Home $home) {
if (!$this->getHomes()->contains($home)) {
$this->getHomes()->add($home);
}
}
使用此Yaml将存储库作为服务注入:
repository.group:
class: Infrastructure\Persistence\DoctrineORM\Repository\GroupRepository
factory: ["@doctrine.orm.default_entity_manager", getRepository]
arguments:
- 'Domain\Entity\Group'
repository.home:
class: Infrastructure\Persistence\DoctrineORM\Repository\HomeRepository
factory: ["@doctrine.orm.default_entity_manager", getRepository]
arguments:
- 'Domain\Entity\Home'
这一直有效到上周,现在当我运行此代码时,它会引发异常:
$group = $groupRepo->find(3); // Group#code = 'A'
$home = new Home('test', $group);
$homeRepo->getEntityManger->persist($home);
$homeRepo->getEntityManger->flush();
它因以下异常而停止:
通过关系'Domain \ Entity \ Home#groups'找到了一个新实体,该关系未配置为级联实体的持久操作:Domain \ Entity \ Group @ 0000000012724e2a00000000138c9399。解决t 他的问题:要么在此未知实体上显式调用EntityManager#persist(),要么配置级联,以在映射中保留此关联,例如@ManyToOne(..,cascade = {“ persist”})。如果你不能 找出哪个实体导致了问题,请实施“ Domain \ Entity \ Group #__ toString()”以获取线索。
很显然,如果我将cascade: ["persist"]
放入Home#groups的yaml配置中,则会得到另一个异常:
SQL异常复制条目[....] INSERT INTO组(代码)VALUES(?):['A']
再次使它工作的唯一方法是使用GroupRepository保留主实体:
$group = $groupRepo->find(3);
$home = new Home('test', $group);
$groupRepo->getEntityManger->persist($home);
$groupRepo->getEntityManger->flush();
一切正常,创建Home并将其显示在Home表中,在连接表中创建一个新行
似乎即使使用“ @ doctrine.orm.default_entity_manager”实例化每个存储库(使用工厂方法),我对于每个存储库也具有不同的EntityManager实例。
$g = $groupRepo->find(1);
$groupRepo->getEntityManager()->contains($g); // true
$user = new User()...
$user->addGroup($g);
$userRepo->getEntityManager()->contains($user); // false
$userRepo->getEntityManager()->contains($user->getGroups()->first()); // false
spl_object_hash($groupRepo->getEntityManger()) // e.g. abc123
spl_object_hash($userRepo->getEntityManager()) // e.g. 456cdef
使用夹具的ObjectManager作品
如果我将上面的代码放在Fixture中,并且直接使用ObjectManager,那么一切都会很好:
class HomesFixtureLoader implements FixtureInterface, ContainerAwareInterface {
// ...
public function load(ObjectManager $manager)
{
$group = $this->container->get('groupRepo')->find(3);
$home = new Home('test', $group)
$manager->persist($home);
$manager->flush();
}
}
在灯具中,我尝试打印spl对象哈希值
public function load(ObjectManager $manager)
{
$groupRepo = $this->container->get('groupRepo');
$homeRepo = $this->container->get('groupRepo');
die(print_r([spl_object_hash($groupRepo->getEntityManager()),
spl_object_hash($homeRepo->getEntityManager()),
spl_object_hash($manager)], true));
}
// RESULT
Array
(
[0] => 00000000024ed69f00000000656e0ce9
[1] => 00000000024ed12000000000656e0ce9
[2] => 00000000024ed69f00000000656e0ce9
)
如您所见,GroupRepository EntityManager与灯具的ObjectManager相同,而HomeRepository的EntityManager不同。这就是为什么我认为使用$ homeRepository保存$ home会出错的原因。
这仅适用于GroupRepository的EntityManager。如果我尝试打印其他存储库的EntityManager实例的spl哈希,则将它们随机地“配对”:
public function load(ObjectManager $manager)
{
$groupRepo = $this->container->get('groupRepo');
$homeRepo = $this->container->get('groupRepo');
$furnitureRepo = $this->container->get('groupRepo');
$detailRepo = $this->container->get('groupRepo');
die(print_r([spl_object_hash($groupRepo->getEntityManager()),
spl_object_hash($homeRepo->getEntityManager()),
spl_object_hash($manager), spl_object_hash($furnitureRepo->getEntityManager(), spl_object_hash($detailRepo->getEntityManager()], true));
}
// RESULT
Array
(
[0] => 0000000016c61829000000003b475624 // A
[1] => 0000000016c61f96000000003b475624 // B
[2] => 0000000016c61829000000003b475624 // A
[3] => 0000000016c61f96000000003b475624 // B
[4] => 0000000016c61829000000003b475624 // A
)
下一个测试是尝试直接从ObjectManager获取存储库
public function load(ObjectManager $manager)
{
$homeRepo = $manager->getRepository('Domain:Home');
$groupRepo = $manager->getRepository('Domain:Group');
$furnitureRepo = $manager->getRepository('Domain:Furniture');
$detailRepo = $manager->getRepository('Domain:Detail');
die(print_r([spl_object_hash($groupRepo->getEntityManager()),
spl_object_hash($homeRepo->getEntityManager()),
spl_object_hash($manager), spl_object_hash($furnitureRepo->getEntityManager(), spl_object_hash($detailRepo->getEntityManager()], true));
}
// RESULT
Array
(
[0] => 0000000025c5506b0000000042ab38bc
[1] => 0000000025c5506b0000000042ab38bc
[2] => 0000000025c5506b0000000042ab38bc
[3] => 0000000025c5506b0000000042ab38bc
[4] => 0000000025c5506b0000000042ab38bc
)
我已经花了很多时间试图了解这里的情况,有人建议吗?
编辑:解决了这种情况-错误/问题的原因
因此,我找到了一种使用 @ doctrine.orm.container_repository_factory 在我的存储库中拥有一致的EntityManager的方法:
repository.group:
class: Infrastructure\Persistence\DoctrineORM\Repository\GroupRepository
factory: ["@doctrine.orm.container_repository_factory", getRepository]
arguments:
- '@doctrine.orm.default_entity_manager'
- 'Domain\Entity\Group'
repository.home:
class: Infrastructure\Persistence\DoctrineORM\Repository\HomeRepository
factory: ["@doctrine.orm.container_repository_factory", getRepository]
arguments:
- '@doctrine.orm.default_entity_manager'
- 'Domain\Entity\Home'
...
public function load(ObjectManager $manager)
{
$groupRepo = $this->container->get('groupRepo');
$homeRepo = $this->container->get('groupRepo');
$furnitureRepo = $this->container->get('groupRepo');
$detailRepo = $this->container->get('groupRepo');
print_r([
spl_object_hash($homeRepo->getEM()),
spl_object_hash($groupRepo->getEM()),
spl_object_hash($furnitureRepo->getEM()),
spl_object_hash($detailRepo->getEM())], true));
}
// RESULT
Array
(
[0] => 0000000066defdbc00000000687b521b
[1] => 0000000066defdbc00000000687b521b
[2] => 0000000066defdbc00000000687b521b
[3] => 0000000066defdbc00000000687b521b
)
现在$userRepo->save($user)
运作良好。
我真的不明白为什么在使用factory: ["@doctrine.orm.default_entity_manager", getRepository]
的同时使用factory: ["@doctrine.orm.container_repository_factory", getRepository]
会在存储库中生成不一致的EntityManager实例的原因。真的不知道这是否是我所遇到的真正问题,因为上周一切运行顺利,经过一番重塑之后,该停止工作了
答案 0 :(得分:0)
我遇到了类似的问题,并且能够通过调试和尝试/错误来达到与您相同的解决方法。
有几种可能的根本原因。
1)Symfony-Doctrine捆绑包中存在一个错误:https://github.com/symfony/symfony/issues/30091-解决方案是将其升级到已修复的Symfony版本
2)用“ doctrine.event_listener”标记服务也会导致此情况。最终在实体管理器和存储库中有2个不同的UnitOfWork实例。 要解决此问题,请使用“ lazy”参数声明事件侦听器。例如:
AppBundle\Service\Foo\Listener:
tags:
- { name: doctrine.event_listener, event: onFlush, method: onFlush, lazy: true }
- { name: doctrine.event_listener, event: postFlush, method: postFlush, lazy: true }