Несовместимость разных экземпляров EntityManager с использованием метода фабрики getRepository () - PullRequest
0 голосов
/ 01 ноября 2018

Привет, у меня есть такая структура: Home N <-> N Groups

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();

Остановится с этим исключением:

Новый объект был обнаружен в связи «Домен \ Entity \ Home # groups», который не был настроен для каскадного сохранения операций для объекта: Domain \ Entity \ Group @ 0000000012724e2a00000000138c9399. Решить т его проблема: либо явным образом вызовите EntityManager # persist () для этого неизвестного объекта, либо настройте каскадное сохранение этой ассоциации в отображении, например @ManyToOne (.., cascade = {"persist"}). Если вы не можете фи Определите, какая сущность вызывает проблему, и реализуйте метод 'Domain \ Entity \ Group #__ toString ()', чтобы получить подсказку.

Очевидно, что если я добавлю cascade: ["persist"] в конфигурацию yaml групп Home #, я получу еще одно исключение:

Исключение SQL. Повторяющаяся запись [....] ВСТАВИТЬ В группы (код) ЗНАЧЕНИЯ (?): ['A']

Единственный способ заставить его работать снова - это использовать GroupRepository для сохранения Home Entity:

$group = $groupRepo->find(3);
$home = new Home('test', $group);
$groupRepo->getEntityManger->persist($home);
$groupRepo->getEntityManger->flush();

И все работает, Дом создан и представлен в домашней таблице, новая строка создается в объединительной таблице

Кажется, что даже если использовать "@ 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

ИСПОЛЬЗУЯ FIXTURE ObjectManager WORKS

Если я помещаю приведенный выше код в 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
)

Как вы можете видеть, EntityManager GroupRepository совпадает с ObjectManager Fixture, тогда как EntityManager HomeRepository отличается. Вот почему я думаю, что сохранение $ home с помощью $ homeRepository пойдет не так.

Это верно только для EntityManager GroupRepository. Если я пытаюсь напечатать Spl-хэш из других репозиториев, экземпляры EntityManager случайным образом «спариваются»:

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

РЕДАКТИРОВАТЬ: РЕШЕНИЕ СИТУАЦИИ - ОШИБКА / ПРОБЛЕМА ОСТАЕТСЯ

Итак, я нашел способ иметь согласованный EntityManager в своих репозиториях, используя @doctrine.orm.container_repository_factory:

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] генерируются несогласованные экземпляры EntityManager в репозиториях, а при использовании factory: ["@doctrine.orm.container_repository_factory", getRepository] лучшая стабильность. Не знаю точно, является ли это реальной проблемой моей ситуации, потому что на прошлой неделе все работало гладко и после некоторого жесткого рефакторинга это перестало работать хорошо

...