Отключение, кеширование и слияние доктрины

Я на Доктрине 2.3. У меня есть следующий запрос:

$em->createQuery('
    SELECT u, c, p
    FROM Entities\User u
    LEFT JOIN u.company c
    LEFT JOIN u.privilege p
    WHERE u.id = :id
')->setParameter('id', $identity)

Затем я беру это, получаю результат (который представляет собой массив, я просто беру первый элемент) и запускаю отрыв $em->detach($result);.

Когда я выхожу из кэша (используя драйвер кэша Doctrine APC), я делаю:

$cacheDriver = new \Doctrine\Common\Cache\ApcCache();
if($cacheDriver->contains($cacheId))
{
    $entity = $cacheDriver->fetch($cacheId);
    $em->merge($entity);
    return $entity;
}

Моя надежда заключалась в том, что это снова активирует загрузку отношений с сущностью, поскольку существует много других отношений, связанных с объектом User, отличным от того, что показано в этом запросе.

Я пытаюсь создать новый объект вроде:

$newEntity = new Entities\ClientType();
$newEntity['param'] = $data;
$newEntitiy['company'] = $this->user['company'];
$em->persist($newEntity);
$em->flush();

Когда я это сделаю, я получаю сообщение об ошибке:

A new entity was found through the relationship 'Entities\ClientType#company' that was not configured to cascade persist operations for entity:
Entities\[email protected] 
To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @ManyToOne(..,cascade={"persist"}). 
If you cannot find out which entity causes the problem implement 'Entities\Company#__toString()' to get a clue.

Это работает отлично, когда я не использую объект компании под пользовательским объектом, который я получил из кеша. Есть ли способ сделать эту работу, поэтому мне не нужно возвращать сущность компании из базы данных каждый раз, когда я хочу использовать ее в отношениях с новым объектом?

Edit: Это то, что у меня есть в объекте User, который имеет дело с этими двумя отношениями:

/**
    * @ManyToOne(targetEntity="Company" , inversedBy="user", cascade={"detach", "merge"})
    */
    protected $company;

    /**
    * @ManyToOne(targetEntity="Privilege" , inversedBy="user", cascade={"detach", "merge"})
    */
    protected $privilege;

Я все еще получаю ту же ошибку.

Второе редактирование: Попытка $em->contains($this->user); и $em->contains($this->user['company']); вернуть false. Что звучит... неправильно.

Ответ 1

При объединении пользователя вы хотите, чтобы связанная компания и привилегия были объединены, исправьте?

Этот процесс называется каскадным:

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html#transitive-persistence-cascade-operations

В пользовательском объекте поместите cascade={"merge"} в аннотацию @ManyToOne (или другой тип определения определения, который вы используете) для $company и $privilege.

И если вы хотите, чтобы вызов отсоединения также был каскадным (рекомендуется), введите cascade={"detach", "merge"}.

p.s.: Не ставьте такие каскады с обеих сторон одной ассоциации, вы создадите бесконечный цикл;)

Edit:

Этот фрагмент кода:

$entity = $cacheDriver->fetch($cacheId);
$em->merge($entity);                      // <-
return $entity;

должен быть:

$entity = $cacheDriver->fetch($cacheId);
$entity = $em->merge($entity);            // <-
return $entity;

Вещь с merge() заключается в том, что она оставляет объект, который вы передаете в качестве аргумента нетронутым, и возвращает объект new, который представляет управляемую версию объекта. Поэтому вы хотите использовать возвращаемое значение, а не аргумент, который вы передали.