Получение "истинного" объекта из прокси-объекта в doctrine2
Doctrine использует прокси-объекты для представления связанных объектов, чтобы облегчить ленивую загрузку. Это действительно классная функция, но она вызывает проблему с чем-то, что я пытаюсь выполнить.
Я настроил свой пользовательский объект так, чтобы все они были связаны с другим объектом, который я буду называть городом. Это соотношение работает нормально.
У меня есть форма, которую мой пользователь заполняет для создания другого объекта, улицы. Улица также связана с объектом города. Вместо того, чтобы мой пользователь выбирал город, когда он заполняет форму, я хочу автоматически установить ее, прежде чем я сохраню объект в моей базе данных.
Я попытался использовать $event->setCity($user->getCity())
, но поскольку $user- > getCity() возвращает прокси-объект, это порождает ошибку. Есть ли функция, которую я могу вызвать из прокси-объекта, чтобы получить реальный?
Примечание. Я знаю, что могу создать пользовательский запрос с соединением, чтобы заставить доктрину фактически загружать связанный объект, но так как это пользователь (с помощью FOSUserBundle), который будет трудно выполнить правильно.
Ответы
Ответ 1
Изменить: Как упоминалось в @flu, этот подход не возвращает "истинный" объект. Однако это может быть полезно в случае, если вам нужны данные из объекта.
Затем вы можете просто получить реальный объект из ObjectManager с помощью некоторого идентификатора.
Мы можем использовать метод __load() из интерфейса прокси-сервера
$proxyObject->__load();
Ответ 2
Это вряд ли поможет в конкретном случае для вопроса, поскольку вы полагаетесь на сторонний модуль, но вы можете предотвратить ленивую загрузку, установив режим "выборки" для вашей организации на "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
Ни один из других ответов, которые я видел здесь, не работал у меня.
Ответ 3
Вот мое решение:
Контекст:
Все мои объекты имеют свойство 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
Я надеюсь, что это может помочь кому-то
Ответ 4
Вы получаете уникальный экземпляр объекта из Doctrine. Если вы запросите его два раза, вы всегда получите тот же объект.
Как следствие, если ваш объект сначала загружен ленивым (например, через @ManyToOne где-то), этот экземпляр объекта будет прокси.
Пример:
У вас есть пользовательский объект, имеющий двунаправленный @OneToOne
в объекте Config...
Случай 1
Запрос пользователя:
- вы получаете реальный экземпляр пользователя
- $user- > config будет содержать прокси
Если позже вы попросите один и тот же объект Config в любой части вашего приложения, вы получите этот прокси.
Случай 2
Вы запрашиваете свой Config, и ваш пользователь никогда не импортировался раньше:
- вы получаете реальный экземпляр Config
- $config- > пользователь будет содержать прокси
Если позже вы попросите один и тот же пользовательский объект в любой части вашего приложения, вы получите этот прокси.
В целом, запрос снова для одного и того же объекта будет по-прежнему заканчиваться прокси-сервером (который является экземпляром вашего Пользователя в любом случае, поскольку сгенерированный прокси-сервер простирается от него).
Если вам нужен действительно второй экземпляр вашего объекта, который является 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;
}
Ответ 5
Это немного неприятное обходное решение:
// $proxyObject = ...
$em->detach($proxyObject);
$entityObject = $em->find(<ENTITY_CLASS>, $proxyObject->getId());
// now you have real entity and not the proxy (entityObject instead of proxyObject)
после этого вы можете заменить ссылку на прокси, если вам нужно иметь ее внутри других объектов
Ответ 6
Доктрина ленивая загрузка очень хороша в своей работе и заменит прокси-объект на реальный, как только вы попытаетесь использовать его или какие-либо его свойства. Если у вас возникли проблемы из-за прокси-объектов (как в моем вопросе), скорее всего, у вас есть ошибка в вашей модели.
Тем не менее, вы можете сказать доктрине, чтобы вытащить все связанные данные, сказав ей "гидратировать":
$query->getResult(Doctrine\ORM\Query::HYDRATE_ARRAY);
Ответ 7
Symfony PropertyNormalizer
решает эту проблему с помощью метода getParent
ReflectionClass
. Если вам нужно осмотреть объект, как для нормализатора, а не просто использовать методы ->get
, вы сможете использовать аналогичный подход.
Похоже, что прокси имеет родителя фактической сущности, поэтому instanceof все еще работает.
Ответ 8
Решение, которое я нашел, состоит в том, чтобы расширить Reflection Hydrator
и использовать новый класс в качестве параметров экстрактора Hal для сложных объектов.
Вы можете найти код здесь:
Синергия прокси-объекта доктрины и зенд-экспрессивного хала
наверное.
Ответ 9
Если вы сначала использовали $em->getReference
, а затем $em->find
, то результатом будет _proxy.
Я рекомендую использовать:
createQueryBuilder ... ->getQuery()
->setHint (Query::HINT_FORCE_PARTIAL_LOAD, true)
->getOneOrNullResult();
Ответ 10
Это преобразует ссылку на реальный объект и выберет все поля.
$entityManager->refresh($proxyObject);