2012-01-12 45 views
16

Digamos que tengo dos entidades en mi proyecto Symfony2: Category y Article (una categoría que tiene muchos artículos).Doctrine2 (Doctrine 2.1) carga ansiosa en Symfony2

En mi CategoryRepository, tengo este método:

findAllDummy(){ 
    return $this->createQueryBuilder('c') 
       ->leftJoin('c.Articles a') 
       ->getQuery()->getResult(); 
} 

si no recuerdo mal, en Symfony1.4 (y la versión correspondiente de Doctrina), los objetos devueltos tendrían su atributo 'artículos' llenado por los objetos correspondientes Article. Ahora, en Symfony2, se devuelven los objetos Proxy.

Así que si recorro los artículos de una categoría específica, se ejecutarán tantas consultas como iteraciones.

foreach($category->getArticles() as $article){ 
    echo $article->getDoctrine() 
       ->getRepository('')getTitle(); 
} 

Entiendo que este es el comportamiento de carga diferida predeterminado de Doctrine2.1.

Pregunta 1: ¿cómo es esta una solución mejor? N dudas en lugar de 1.

Me trataron de obligar a la carga con ganas de la siguiente manera:

findAllDummy(){ 
    return $this->createQueryBuilder('c') 
       ->leftJoin('c.articles a') 
       ->getQuery() 
       ->setFetchMode('Category', 'articles', 'EAGER') 
       ->getResult(); 
} 

Pero el resultado sigue siendo el mismo.

Pregunta 2: cómo forzar la carga ansiosa en Doctrine2?

Respuesta

18

Te estás uniendo a una mesa pero no estás seleccionando nada de ella. Agregue ->addSelect('a') a su generador de consultas. Considere dos siguientes consultas SQL para entender la diferencia: unirse

SELECT a.id, a.title 
FROM article a 
JOIN category c ON a.category_id = c.id 
WHERE a.id = 123; 

SELECT a.id, a.title, c.id, c.name 
FROM article a 
JOIN category c ON a.category_id = c.id 
WHERE a.id = 123; 

Eager/perezoso no tiene nada que ver con consultas DQL. Define lo que debe cargarse cuando usa $articleRepository->find(123).

+0

@Crozin Todavía estoy un poco confundido ... cuando dices "define lo que se debe cargar", ¿te refieres a lo que está hidratado? –

+1

@SamSelikoff Sí, si define la relación 'fetch = EAGER' en artículos, todos los artículos se cargarán (e hidratarán) cuando llame a' categoryRepository-> findOne (321) ' – Crozin

+0

¿Cuál es la diferencia entre cargar e hidratar? –

-2

Es una solución mejor porque las uniones son un proceso mucho más costoso que una simple consulta. Si bien puede parecer ineficiente, no es un gran desperdicio, y se vuelve más eficiente rápidamente cuando no está cargando cada bit de cada objeto relacionado.

1

En la parte donde se intenta "fuerza de carga ansiosa" el problema podría ser que utilice el método fetchMode con el tipo de variable incorrecto para el argumento $fetchMode. Pasa una cadena 'EAGER' pero el método no espera una cadena sino un entero.

El método espera constantes de la clase ClassMetadata:

/** 
* Specifies that an association is to be fetched when it is first accessed. 
*/ 
const FETCH_LAZY = 2; 

/** 
* Specifies that an association is to be fetched when the owner of the 
* association is fetched. 
*/ 
const FETCH_EAGER = 3; 

En la documentación capítulo Doctrina 14.7.6.6. Cambio temporal de recuperacion en DQL se puede ver un ejemplo sobre cómo utilizar este:

$query->setFetchMode("MyProject\User", "address", \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER); 

manera que vaya, ya sea una referencia a la constante o un número entero que se corresponde con el modo que desea utilizar.

0

Como dice en el doctrine docs, la carga ansiosa en este caso no hará ninguna diferencia porque tiene una relación uno a muchos entre categoría y artículo.

Para las relaciones de uno a muchos, cambiar el modo de búsqueda a ansioso hará que se ejecute una consulta para cada entidad raíz cargada. Esto no mejora el modo de recuperación diferida, que también iniciará las asociaciones una por una una vez que se acceda a ellas.

Así que, contrariamente a lo que @Crozin ha dicho, todavía puede hacer cargas ansiosas en DQL. Si tiene una relación uno a uno o muchos a uno, la carga ansiosa resolverá el problema de realizar consultas adicionales. Sin embargo, para resolver su problema, en este caso debe usar ->addSelect('a') como se menciona en @Crozin.

Cuestiones relacionadas