Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Doctrine QueryBuilder indexBy on joined class - p is already defined Error

Using symfony2/doctrine2, I have a hard time defining an index for my query.

My code :

    $queryBuilder = $this->_em
        ->createQueryBuilder()
        ->select('u, uis, cost, p, stock')
        ->from('AppBundle:FoodAnalytics\UserIngredient', 'u', 'p.id')
        ->leftJoin('u.product', 'p')
        ->leftJoin('u.numberObjects', 'stock')
        ->leftJoin('u.userIngredientSuppliers', 'uis')
        ->leftJoin('uis.numberObjects', 'cost')
        ->where('u.user = ?1')
        ->setParameter(1, $portfolioUser)
        ;

I get the following error :

[Semantical Error] line 0, col 110 near 'p LEFT JOIN u.numberObjects': Error: 'p' is already defined.
500 Internal Server Error - QueryException
1 linked Exception: QueryException »

[1/2] QueryException: SELECT u, uis, cost, p, stock FROM AppBundle:FoodAnalytics\UserIngredient u INDEX BY p.id LEFT JOIN u.product p LEFT JOIN u.numberObjects stock LEFT JOIN u.userIngredientSuppliers uis LEFT JOIN uis.numberObjects cost WHERE u.user = ?1   +

Using u.product I get the following error :

[Semantical Error] line 0, col 87 near 'product LEFT': Error: Invalid PathExpression. Must be a StateFieldPathExpression.
500 Internal Server Error - QueryException
1 linked Exception: QueryException »

Using just product, I get the following error:

[Semantical Error] line 0, col 85 near 'product LEFT': Error: 'product' does not point to a Class.
500 Internal Server Error - QueryException
1 linked Exception: QueryException »

It works fine if I use u.id Can I only use index by a field from the same table ?

What can I do to make it work ?

Thansk a lot !

EDIT :

As a temporary fix I am using :

    $result = $queryBuilder->getQuery()->getResult();
    $result = array_combine(array_map(function(UserIngredient $userIngredient){
                return $userIngredient->getProduct()->getId();
            }, $result), $result);
    return $result;
like image 675
Sébastien Avatar asked Apr 23 '15 15:04

Sébastien


1 Answers

The attribute indexBy only apply to the entity you are currently selecting, as you have guessed (and AFAIK); so in your case you could only index by u.id.

This makes sense to me, since indexing from other entity really mess up the returned results. The only situation in which you'll get proper result is when:

  • all the join from the main entity and the "target" indexBy entity are one-to-one;
  • all the join are INNER JOIN;
  • the indexBy column of the resultset is unique.

In every other case you loose some instance reference during hydration.

In your example, you are using LEFT JOIN: what will happen to all entities without product? All discarded, so the LEFT JOIN would have worker like an INNER JOIN. Also, your workaround suggest that you want to combine entities with the same index, but this is not how Doctrine work: in Doctrine an indexBy column MUST be unique. See the official docs for more info.

Note that you could indexBy p.id in your JOIN clause, but this has different meaning. It means that when hydrating the UserIngredients instances, the collection of product should be indexed by id.

So, my suggestion is to follow one of this two solution:

  • give up on Doctrine indexBy, and use your workaraound;
  • If you have the inverse association Product -> UserIngredients, use Product as the main entity of the query (the from clause, and first to appear in select), and then index by p.id.

Which one to use depends on your business logic, but I generally prefer the second solution, since the first generate multiple-level array, which are better represented by entity relations.

Hope this help! :)

like image 195
giosh94mhz Avatar answered Nov 20 '22 07:11

giosh94mhz