Получение «истинного» объекта из прокси-объекта в doctrine2 - PullRequest
33 голосов
/ 01 декабря 2011

Doctrine использует прокси-объекты для представления связанных объектов, чтобы облегчить отложенную загрузку.Это действительно классная функция, но она вызывает проблему с чем-то, что я пытаюсь выполнить.

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

У меня есть форма, которую мой пользователь заполняет для создания другого объекта, улицы.Улица также связана с городским объектом.Вместо того, чтобы мой пользователь выбирал город при заполнении формы, я хочу автоматически установить его перед сохранением объекта в моей базе данных.

Я пытался использовать $event->setCity($user->getCity()), но так как $ user-> getCity() возвращает прокси-объект, это генерирует ошибку.Есть ли функция, которую я могу вызвать из прокси-объекта, чтобы получить реальный объект?

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

Ответы [ 9 ]

15 голосов
/ 23 декабря 2015

Это вряд ли поможет в конкретном случае для вопроса, поскольку вы полагаетесь на сторонний модуль, но вы можете предотвратить отложенную загрузку, установив «режим извлечения» для вашей сущности на «EAGER».

User:
    ManyToOne:
        city:
            fetch: EAGER

Это также может быть обработано аннотациями:

@ManyToOne(targetEntity="city", fetch="EAGER")
@JoinColumn(name="city", referencedColumnName="id")

См. http://docs.doctrine -project.org / projects / doctrine-orm / en / latest / reference /annotations-reference.html # annref-manytoone

Ни один из других ответов, которые я здесь видел, не работал для меня.

13 голосов
/ 01 июля 2014

Редактировать: Как уже упоминалось @flu, этот подход не возвращает "истинный" объект. Однако это может быть полезно в случае, если вам нужны данные от объекта. Затем вы можете просто получить реальный объект из ObjectManager с помощью некоторых идентификаторов.


Мы можем использовать метод __load () из интерфейса Proxy

$proxyObject->__load();
2 голосов
/ 31 января 2017

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

Как следствие, если ваша сущность сначала загружается лениво (например, через @ManyToOne где-нибудь), этот экземпляр сущности будет прокси.

Пример:

У вас есть объект User, имеющий двунаправленный @OneToOne в объекте Config ...

Дело 1

Вы запрашиваете у вашего пользователя:

  • вы получаете настоящий экземпляр пользователя
  • $ user-> config будет содержать прокси

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

Дело 2

Вы запрашиваете свою конфигурацию, и ваш пользователь никогда не был импортирован:

  • вы получаете реальный экземпляр Config
  • $ config-> user будет содержать прокси

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


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

Если вам действительно нужен второй экземпляр вашей сущности, который является real (если некоторая логика вашего приложения имеет get_class, который вы не можете заменить на instanceof, например, ), вы можете попробовать поиграть с $em->detach(), но это будет кошмар (и, следовательно, ваше приложение может вести себя даже с большей магией, чем Doctrine уже приносит).

Решением (исходя из моей темной стороны, я полагаю) может быть воссоздание неуправляемой сущности вручную.

public function getRealEntity($proxy)
{
    if ($proxy instanceof Doctrine\ORM\Proxy\Proxy) {
        $metadata              = $this->getManager()->getMetadataFactory()->getMetadataFor(get_class($proxy));
        $class                 = $metadata->getName();
        $entity                = new $class();
        $reflectionSourceClass = new \ReflectionClass($proxy);
        $reflectionTargetClass = new \ReflectionClass($entity);
        foreach ($metadata->getFieldNames() as $fieldName) {
            $reflectionPropertySource = $reflectionSourceClass->getProperty($fieldName);
            $reflectionPropertySource->setAccessible(true);
            $reflectionPropertyTarget = $reflectionTargetClass->getProperty($fieldName);
            $reflectionPropertyTarget->setAccessible(true);
            $reflectionPropertyTarget->setValue($entity, $reflectionPropertySource->getValue($proxy));
        }

        return $entity;
    }

    return $proxy;
}
2 голосов
/ 30 сентября 2016

Вот мое решение:

Контекст:

Все мои сущности имеют id свойство и getId() метод


Решение:

$em = $this->getDoctrine()->getManager();

// 1 -> get the proxy object class name
$proxy_class_name = get_class($proxyObject);

// 2 -> get the real object class name
$class_name = $em->getClassMetadata($proxy_class_name)->rootEntityName;

// 3 -> get the real object
$object = $em->find($class_name, $proxyObject->getId());

Проблема:

Это решение не работает, если свойство id и метод getId() находятся в Trait классе

Я надеюсь, что это может помочь кому-то

1 голос
/ 21 апреля 2017

Это немного неприятный обходной путь, который выдает:

// $proxyObject = ...

$em->detach($proxyObject);
$entityObject = $em->find(<ENTITY_CLASS>, $proxyObject->getId());

// now you have real entity and not the proxy (entityObject instead of proxyObject)

, после чего вы можете заменить ссылку на прокси, если вам нужно, чтобы она была внутри других сущностей

0 голосов
/ 14 апреля 2019

Решение, которое я нашел, состоит в расширении Reflection Hydrator и использовании нового класса в качестве параметров экстрактора Hal для сложных объектов.

Вы можете найти код здесь:

Синергия прокси-объекта учения и зенд-экспрессивного хала

наверное.

0 голосов
/ 21 января 2019

Symfony PropertyNormalizer решает эту проблему, используя метод ReflectionClass getParent. Если вам нужно осмотреть объект, как в случае нормализатора, а не просто использовать методы ->get, вы сможете использовать аналогичный подход.

Похоже, у Прокси есть родительский объект фактической сущности, поэтому instanceof все еще работает.

0 голосов
/ 05 мая 2015

Это преобразует ссылку в реальный объект и извлекает все поля.

$entityManager->refresh($proxyObject);
0 голосов
/ 15 апреля 2013

Ленивая загрузка доктрин отлично справляется со своей задачей и заменит прокси-объект реальным, как только вы попытаетесь использовать его или любое из его свойств. Если у вас возникли проблемы из-за объектов прокси (как я и сделал в моем вопросе), скорее всего, в вашей модели есть ошибка.

Тем не менее, вы можете сказать доктрине, чтобы она вытягивала все связанные данные, сказав, что она "гидратируется": $query->getResult(Doctrine\ORM\Query::HYDRATE_ARRAY);

...