Pregunta Obtener un objeto "verdadero" de un objeto proxy en doctrine2


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 dado que $ 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.


32
2017-12-01 07:14


origen


Respuestas:


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();

11
2017-07-01 03:23



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")

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

Ninguna de las otras respuestas que he visto aquí funcionó para mí.


11
2017-12-23 02:01



Aquí está mi solución:

Contexto:

Todas mis entidades tienen id propiedad y getId() método


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 id propiedad y getId() método están en una Trait clase

Espero que pueda ayudar a alguien


2
2017-09-30 15:59



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 bidireccional @OneToOne en una entidad Config ...

Caso 1

Usted solicita su usuario:

  • obtienes un real instancia 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 un real instancia de configuración
  • $ 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 tu De Verdad necesita una segunda instancia de su entidad que es un real uno (si parte de la lógica de su aplicación get_class que no puedes reemplazar por instanceof por ejemplo), puedes 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;
}

1
2018-01-31 07:19



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


1
2018-04-21 09:34



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, puedes decirle a doctrine que extraiga todos los datos relacionados diciéndole que "hidrate": $query->getResult(Doctrine\ORM\Query::HYDRATE_ARRAY);


0
2018-04-15 13:26



Esto convertirá la referencia a un objeto real y buscará todos los campos.

$entityManager->refresh($proxyObject);

-3
2018-05-05 16:50