¿Qué es un proxy en Doctrine 2?

112

Acabo de terminar de leer toda la documentación de Doctrine 2, comencé mi propia caja de arena, entendí la mayoría de los principios, pero todavía hay una pregunta y no pude encontrar una explicación completa en el documento.

  1. ¿Qué son las Proxyclases?
  2. ¿Cuándo debería usarlos sobre entidades?

Por lo que tengo entendido, las clases de proxy agregan una capa que le permite agregar algunas otras características a sus entidades, pero ¿por qué usar un proxy en lugar de implementar los métodos en sí mismos en la clase de entidad?

Jérémy
fuente

Respuestas:

160

ACTUALIZAR

Esta respuesta contiene información incorrecta sobre las diferencias entre los objetos proxy y los objetos parciales. Consulte la respuesta de @ Kontrollfreak para obtener más detalles: https://stackoverflow.com/a/17787070/252591


Los objetos proxy se utilizan siempre que su consulta no devuelve todos los datos necesarios para crear una entidad. Imagina el siguiente escenario:

@Entity
class User {
     @Column protected $id;
     @Column protected $username;
     @Column protected $firstname;
     @Column protected $lastname;

     // bunch of setters/getters here
}

DQL query:

SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id

Como se puede ver esta consulta no devuelve firstnamey lastnamepropiedades, por lo tanto, no puede crear Userobjeto. La creación de una entidad incompleta podría dar lugar a errores inesperados.

Es por eso que Doctrine creará un UserProxyobjeto que admita la carga diferida. Cuando intente acceder a la firstnamepropiedad (que no está cargada), primero cargará ese valor de la base de datos.


Quiero decir, ¿por qué debería usar un proxy?

Siempre debe escribir su código como si no usara objetos proxy en absoluto. Pueden tratarse como objetos internos utilizados por Doctrine.

¿Por qué la carga diferida no se puede implementar en la propia Entidad?

Técnicamente podría serlo, pero eche un vistazo a la clase de algún objeto proxy aleatorio. Está lleno de código sucio, uf. Es bueno tener un código limpio en sus entidades.

¿Puede proporcionarme un caso de uso?

Está mostrando una lista de los últimos 25 artículos y desea mostrar los detalles del primero. Cada uno de ellos contiene una gran cantidad de texto, por lo que recuperar todos esos datos sería una pérdida de memoria. Por eso no obtiene datos innecesarios.

SELECT a.title, a.createdAt
FROM Entity\Article a
ORDER BY a.createdAt DESC
LIMIT 25

$isFirst = true;
foreach ($articles as $article) {
    echo $article->getTitle();
    echo $article->getCreatedAt();

    if ($isFirst) {
        echo $article->getContent(); // Article::content is not loaded so it is transparently loaded 
                                     // for this single article.

        $isFirst = false;
    }
}
Crozin
fuente
Gracias por tu respuesta, ¿en qué es diferente con Partial Object? Quiero decir, ¿por qué debería usar un proxy? ¿Por qué la carga diferida no se puede implementar en la propia Entidad? ¿Puede proporcionarme un caso de uso?
Jérémy
1
Los objetos parciales y los objetos proxy son lo mismo: pueden tratarse como sinónimos. En cuanto al resto de preguntas, consulte mi respuesta actualizada.
Crozin
1
No entiendo por qué la doctrina no puede crear el objeto si solo tiene la mitad de las propiedades. En php puedo crear un objeto incluso si no configuro todas las propiedades.
lijadoras
1
Esta es una respuesta totalmente asombrosa y debería estar en la documentación.
Jimbo
7
Esta respuesta contiene algunos conceptos erróneos graves sobre los proxies y los objetos parciales. Vea mi respuesta para entender por qué.
Kontrollfreak
81

Proxies

Un proxy de Doctrine es solo un contenedor que extiende una clase de entidad para proporcionarle Carga diferida.

De forma predeterminada, cuando le solicita al Administrador de entidades una entidad que está asociada con otra entidad, la entidad asociada no se cargará desde la base de datos, sino que se incluirá en un objeto proxy. Cuando su aplicación luego solicita una propiedad o llama a un método de esta entidad proxed, Doctrine cargará la entidad desde la base de datos (excepto cuando solicite la ID, que siempre es conocida por el proxy).

Esto sucede de forma totalmente transparente para su aplicación debido al hecho de que el proxy extiende su clase de entidad.

Doctrine hidratará de forma predeterminada las asociaciones como proxies de carga diferida si no las JOINincluye en su consulta o establece el modo de recuperación en EAGER.


Ahora debo agregar esto porque no tengo suficiente reputación para comentar en todas partes:

Desafortunadamente, la respuesta de Crozin contiene información errónea.

Si ejecuta una consulta DQL como

SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id

no obtendrá un objeto de entidad (proxy), sino una matriz asociativa. Por lo tanto, no es posible cargar de forma diferida propiedades adicionales.

Con esto en mente, se llega a la conclusión de que el ejemplo de caso de uso tampoco funcionará. El DQL tendría que cambiarse a algo como esto para poder acceder $articlecomo objeto:

SELECT a FROM Entity\Article a ORDER BY a.createdAt DESC LIMIT 25

Y la propiedad devuelta por getContent()tendría que ser una asociación para no cargar las propiedades de contenido de las 25 entidades.


Objetos parciales

Si desea cargar parcialmente propiedades de entidad que no son asociaciones, debe decirle a esta Doctrine explícitamente:

SELECT partial u.{id, username} FROM Entity\User u WHERE u.id = :id

Esto le da un objeto de entidad parcialmente cargado.

¡Pero tenga en cuenta que los objetos parciales no son proxies! La carga diferida no se aplica a ellos. Por lo tanto, el uso de objetos parciales es generalmente peligroso y debe evitarse. Leer más: Objetos parciales - documentación de Doctrine 2 ORM 2

Kontrollfreak
fuente
1
Gracias, esto proporciona muchos más detalles sobre cómo Doctrine usa Proxies y Objetos parciales que la respuesta aceptada. Y la referencia a los documentos también es útil.
Sean the Bean
1
También como referencia, aquí está la sección de los documentos sobre los objetos Proxy: doctrine-orm.readthedocs.org/en/latest/reference/…
Sean the Bean
Entonces, cuando se realiza una carga ansiosa, ¿se trata básicamente de agregar conjuntos de resultados?