Привет, у меня есть такая структура: 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]
лучшая стабильность. Не знаю точно, является ли это реальной проблемой моей ситуации, потому что на прошлой неделе все работало гладко и после некоторого жесткого рефакторинга это перестало работать хорошо