entitymanagerinterface - generate entity symfony
Obtener un objeto "verdadero" de un objeto proxy en doctrine2 (7)
Aquí está mi solución:
Contexto:
Todas mis entidades tienen la propiedad id
y el método getId()
Solución:
$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());
Problema:
Esta solución no funciona si la propiedad id
y el método getId()
están en una clase Trait
Espero que pueda ayudar a alguien
Doctrine usa objetos proxy para representar objetos relacionados para facilitar la carga diferida. Esta es una característica realmente genial, pero está causando un problema con algo que estoy tratando de lograr.
Personalicé mi objeto de usuario para que todos estén relacionados con un objeto diferente, al que llamaré ciudad. Esta relación está funcionando bien.
Tengo un formulario que mi usuario rellena para generar otro objeto, la calle. La calle también está relacionada con el objeto de la ciudad. En lugar de hacer que mi usuario seleccione la ciudad cuando completan el formulario, quiero configurarlo automáticamente antes de que persista el objeto en mi base de datos.
Intenté usar $event->setCity($user->getCity())
, pero como $ user-> getCity () devuelve un objeto proxy, esto genera un error. ¿Hay una función a la que pueda llamar desde el objeto proxy para obtener la verdadera?
Nota: Soy consciente de que puedo crear una consulta personalizada con una combinación para forzar a la doctrina a que cargue realmente el objeto relacionado, pero dado que este es el usuario (que usa FOSUserBundle), sería difícil hacerlo correctamente.
Es poco probable que esto ayude en la instancia específica para la pregunta, ya que se basa en un módulo de terceros, pero puede evitar la carga lenta estableciendo el "modo de búsqueda" para su entidad en "EAGER".
User:
ManyToOne:
city:
fetch: EAGER
Esto también se puede manejar mediante anotaciones:
@ManyToOne(targetEntity="city", fetch="EAGER")
@JoinColumn(name="city", referencedColumnName="id")
Ninguna de las otras respuestas que he visto aquí funcionó para mí.
Esta es una solución un poco desagradable a ese problema:
// $proxyObject = ...
$em->detach($proxyObject);
$entityObject = $em->find(<ENTITY_CLASS>, $proxyObject->getId());
// now you have real entity and not the proxy (entityObject instead of proxyObject)
después de eso puede reemplazar la referencia de proxy si necesita tenerla dentro de otras entidades
Esto convertirá la referencia a un objeto real y buscará todos los campos.
$entityManager->refresh($proxyObject);
La carga diferida de Doctrines es muy buena en su trabajo y reemplazará un objeto proxy por uno real tan pronto como intente usarlo o cualquiera de sus propiedades. Si tiene problemas debido a los objetos proxy (como hice en mi pregunta), es muy probable que tenga un error en su modelo.
Dicho esto, puede decirle a doctrine que extraiga todos los datos relacionados diciéndole que " $query->getResult(Doctrine/ORM/Query::HYDRATE_ARRAY);
": $query->getResult(Doctrine/ORM/Query::HYDRATE_ARRAY);
Obtienes una instancia única de una entidad de Doctrine. Si lo solicita dos veces, siempre obtendrá el mismo objeto.
Como consecuencia, si su entidad está cargada de pereza primero (a través de @ManyToOne en alguna parte, por ejemplo), esta instancia de entidad será un Proxy.
Ejemplo:
Usted tiene una entidad de usuario que tiene un @OneToOne
bidireccional en una entidad Config ...
Caso 1
Usted solicita su usuario:
- obtienes una instancia real de Usuario
- $ user-> config contendrá un Proxy
Si más adelante solicita la misma entidad de configuración en cualquier parte de su aplicación, terminará con ese proxy.
Caso 2
Solicita su configuración y su usuario nunca se ha importado antes:
- obtienes una instancia real de Config
- $ config-> usuario contendrá un Proxy
Si más tarde solicita la misma entidad de usuario en cualquier parte de su aplicación, terminará con ese proxy.
En general, consultar nuevamente para la misma entidad aún terminará en un proxy (que es una instancia de su Usuario de todos modos, porque el proxy generado se extiende desde él).
Si realmente necesita una segunda instancia de su entidad que sea real
(si parte de la lógica de su aplicación es una get_class
que no puede reemplazar por instanceof
por ejemplo), puede intentar jugar con $em->detach()
pero será una pesadilla (y, por lo tanto, su aplicación se comportará con más magia que la que ya trajo Doctrine).
Una solución (desde mi lado oscuro, supongo) puede estar recreando una entidad no gestionada manualmente.
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;
}
Editar: Como se menciona por @flu este enfoque, no devuelva el objeto "verdadero". Sin embargo, puede ser útil en caso de que necesite los datos del objeto. Entonces, puede obtener el objeto real de ObjectManager por parte de la identidad.
Podemos usar el método __load () desde la interfaz Proxy
$proxyObject->__load();