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

1

13

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;
Post answered 23/4, 2015 at 15:11 Comment(6)
I'm not sure if you could index an entity with using a field from related entity.Stripper
I might be completely wrong, but you could try to index by product.id instead of p.id. Also, this: doctrine-project.org/jira/browse/DDC-1180Stripper
ok, product.id does not work. your reference however suggests a fix has been implemented but I've not yet found the corresponding solution. Have you?Marcy
According to PR, this could work: -from('AppBundle:FoodAnalytics\UserIngredient', 'u', 'product'). Does it?Stripper
Is the goal of specifying the index by to start from the product table and then join the UserIngredient table? If Product.Id is the primary key, you could start from Product and not need to specify the index, then do a left join with UserIngredient.Felicefelicia
@JovanPerovic, I've tried alternative expressions, all result in an error, see my edit. George, I could start from the product and only join the right userIngredients but that would not be as neat as using an indexBy. Basically, in my model here, a useringredient is a product attached to user parameters which I try to access. Afterwards I'd have to call $product->getUserIngredients()->first()...Marcy
S
5

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! :)

Sandell answered 24/4, 2015 at 8:54 Comment(3)
well, interesting indeed! in my case I have a unique constraints that guarantees there is no more than one useringredient per product and user, so we have the need to index as if we had a OneToOne relationship except it's not. Given the PR Jovan talked about, I guess we're right to expect it could be done... still searching a better solutionMarcy
I don't think that DDC-1180 relate to your issue. This is about "entity's column which is also a foreign key", so my consideration still apply to this case. In fact, the issue has been fixed and merged in Doctrine 2.1, and you can use it right now.Sandell
(oops clicked enter too fast) Also consider that even if it might be done (but I don't think it will, due to many precondition), it still don't apply to your case, since leftJoin breaks your logic as you cannot have multiple values bound to null-key entry.Sandell

© 2022 - 2024 — McMap. All rights reserved.