使用getRepository()工厂方法的EntityManager不同实例不一致,

时间:2018-11-01 00:48:02

标签: php symfony doctrine

嗨,我有这样的结构: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
)

我已经花了很多时间试图了解这里的情况,有人建议吗?

  • PHP 7.2.7
  • 主义/规范v2.6.1和v2.6.2
  • symfony / symfony v3.4.11

编辑:解决了这种情况-错误/问题的原因

因此,我找到了一种使用 @ 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实例的原因。真的不知道这是否是我所遇到的真正问题,因为上周一切运行顺利,经过一番重塑之后,该停止工作了

1 个答案:

答案 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 }