How to get partial result from doctrine query buil

2019-02-14 22:17发布

问题:

I have a product entity in which it has an array as attributes:

     /**
     * @ORM\OneToMany(targetEntity="Shopious\MainBundle\Entity\ProductPicture", mappedBy="product", cascade={"persist","remove"})
     */
    protected $pictures;

    /** 
    * @Accessor(getter="getCover") 
    */
    private $cover;
    public function getCover()
    {
        if($this->pictures->count() > 0) {
            return $this->pictures[0];
        }
        return new ProductPicture();
    }

Now in my query builder, I have the following code:

 $query = $em->createQueryBuilder()->select('p')
            ->from("SiteMainBundle:Product", 'p')
            ->innerJoin('p.category', 'c')
            ->innerJoin('p.shop', 'shop')
            ;

The issue here is that I don't want to be selecting all of p's attribute. So I only wanted to get the first ProductPicture in the pictures array (in my case above it's similar to the getCover() method). How do I do this?

So far I can filter out the the partial attributes that I want by doing:

 $query = $em->createQueryBuilder()->select('p.name, p.id')
                ->from("SiteMainBundle:Product", 'p')
                ->innerJoin('p.category', 'c')
                ->innerJoin('p.shop', 'shop')
                ->innerJoin('p.pictures', 'pictures')
                ;

so in the example above I have done inner joined on the pictures, but how do I get the first element from here?

In conclusion, my question is how do I select/query the first ProductPicture in the pictures array using the query builder? Because when I do:

$query = $em->createQueryBuilder()->select('p')

it returns the whole product attributes, but I don't want the whole product attributes.. I only wanted some of them, such as the id, name, etc. However one of the product attributes is actually an entity (which is the ProductPicture), so how do I return this in the select statement?

EDIT:

Here's a SQL equivalent on how the pictures should be inner joined:

SELECT * 
FROM  `product` 
JOIN  `product_picture` ON  `product`.id =  `product_picture`.product_id
WHERE  `product`.id =100
LIMIT 1

回答1:

Try something like this, if it's a one to many, the normal mySQL behaviour is returning several records with redundant product data, if the same case happens here, then only returning the first record should do the trick.

PS: assuming the ProductPicture entity has a url property that you want to get

$query = $em->createQueryBuilder()->select('p.id, p.name, pictures.url')
            ->from("SiteMainBundle:Product", 'p')
            ->innerJoin('p.category', 'c')
            ->innerJoin('p.shop', 'shop')
            ->innerJoin('p.pictures', 'pictures')
            ;


回答2:

Add a custom repository method with the DQL for your entity, then call it from the controller

You can name the repository method whatever you want, for this example I'm using findProductWithPicture

class ProductRepository extends EntityRepository
{
    /**
     * @param integer $id
     */
    public function findProductWithPicture($id)
    {
        $dql = <<<SQL
SELECT
    p.id    id,
    p.name  name,
    q       picture
FROM
    Shopious\MainBundle\Entity\ProductPicture q,
    Shopious\MainBundle\Entity\Product p
WHERE
    p.id        = :picture_id  AND
    q.product   = p.id
SQL;

        $query = $this->_em->createQuery($dql)->setParameter('picture_id', $id);

        return $query->setMaxResults(1)->getResult();
    }
}

To use this from the controller

$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('ShopiousMainBundle:Product')->findProductWithPicture($id);

return $this->render('ShopiousMainBundle:Product:show.html.twig', array(
    'product' => $product[0]
));

In the rendered Twig template, you can access them like so

<p>{{ product.id }}
<p>{{ product.name }}
<p>{{ product.picture.whatever_property }}