es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Doctrina indica al constructor de consultas que evite la carga diferida.

Tengo 2 entidades, “Product” y “Provider”, un producto puede tener varios proveedores, así que entre el producto y el proveedor tengo una relación uno a muchos.

“Product”:

/**
 * @ORM\Entity()
 */
class Product
{
    /**
     * @ORM\Id()
     * @ORM\Column(type="integer")
     */
    public int $id;

    /**
     * @ORM\Column(type="string", length=100)
     */
    public string $name;

    /**
     * @ORM\OneToMany(
     *     targetEntity="App\Entity\Provider",
     *     mappedBy="product",
     *     cascade={"persist", "remove"},
     *     orphanRemoval=true
     * )
     */
    public Collection $providers;

    public function __construct()
    {
        $this->providers = new ArrayCollection();
    }
}

“Provider”:

/**
 * @ORM\Entity()
 */
class Provider
{
    /**
     * @ORM\Id()
     * @ORM\Column(type="integer")
     */
    public int $id;

    /**
     * @ORM\Column(type="boolean")
     */
    public bool $available;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Product", inversedBy="providers")
     * @ORM\JoinColumn(name="product_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
     */
    public Product $product;
}

Y necesito obtener el producto por su id y todos sus proveedores disponibles. Para eso, tengo un repository y una consulta así:

class ProductRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Product::class);
    }

    public function getProductWithActionProviders(int $productId): Product
    {
        $qb = $this->createQueryBuilder('product')
            ->innerJoin('product.providers', 'pp')
            ->addSelect('pp');
        $qb->where($qb->expr()->eq('pp.available', ':true'))
            ->setParameter('true', true, Types::BOOLEAN);
        $qb->andWhere($qb->expr()->eq('product.id', ':productId'))
            ->setParameter('productId', $productId, Types::INTEGER);

        return $qb->getQuery()->getSingleResult();
    }
}

Y como resultado sigue devolviendo todos los proveedores, independientemente de si están disponibles o no. No estoy seguro, pero creo que se debe a una carga diferida (lazy loading) y a una consulta interna que solo devuelve los proveedores disponibles, pero cuando se llama a la entidad para obtener los proveedores, carga diferida todos los proveedores que no se han recuperado. Aquí está la consulta SQL generada:

SELECT p0_.id AS id_0, p0_.name AS name_1, p1_.id AS id_11, 
p2_.available AS available_19 
FROM products b0_ 
INNER JOIN providers p1_ ON p1_.product_id = p0_.id 
WHERE p1_.available = ? AND p0_.id = ?

¿Cómo puedo evitar cargar proveedores que no son necesarios? Sé que puedo configurar la estrategia de carga (loading strategy) en “Eager” en la descripción de relaciones de la entidad, pero no lo quiero desactivar de ninguna manera. ¡Gracias!

Tags:  ,

Answer

  1. Avatar for davy.ai

    Parece que el problema está relacionado con la carga diferida. Para evitar cargar proveedores que no se necesitan, una solución posible es utilizar la palabra clave FETCH en la consulta de DQL para forzar la carga ávida de la colección proveedores con la condición de filtro deseada.

    Puede actualizar el método getProductWithActionProviders en la clase ProductRepository de la siguiente manera:

    public function getProductWithActionProviders(int $productId): Product
    {
        $qb = $this->createQueryBuilder('product')
            ->leftJoin('product.providers', 'provider')
            ->addSelect('provider')
            ->where('product.id = :productId')
            ->andWhere('provider.available = :available')
            ->setParameter('productId', $productId)
            ->setParameter('available', true);
    
        $query = $qb->getQuery();
    
        // Forzar la carga ávida de proveedores con la condición disponible
        $query->setFetchMode('App\Entity\Product', 'providers', ClassMetadata::FETCH_EAGER, $qb);
    
        return $query->getOneOrNullResult();
    }
    

    Esto generará una consulta que incluye la condición de filtro para cargar ávidamente la colección de proveedores con la condición disponible, y devuelve solo el objeto de producto con el ID correspondiente y los proveedores cargados ávidamente.

Comments are closed.