Doctrine QueryBuilder indexBy в объединенном классе - p уже определен Ошибка

Используя symfony2/doctrine2, мне сложно определить индекс для моего запроса.

Мой код:

    $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)
        ;

Я получаю следующую ошибку:

[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   +

Используя u.product, я получаю следующую ошибку:

[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 »

Используя только product, я получаю следующую ошибку:

[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 »

Он отлично работает, если я использую u.id Могу ли я использовать индекс только в поле из той же таблицы?

Что я могу сделать, чтобы он работал?

Танск много!

ИЗМЕНИТЬ:

Как временное исправление, которое я использую:

    $result = $queryBuilder->getQuery()->getResult();
    $result = array_combine(array_map(function(UserIngredient $userIngredient){
                return $userIngredient->getProduct()->getId();
            }, $result), $result);
    return $result;

Ответ 1

Атрибут indexBy применяется только к сущности, которую вы сейчас выбираете, как вы уже догадались (и AFAIK); поэтому в вашем случае вы можете индексировать только u.id.

Это имеет смысл для меня, поскольку индексирование с другого объекта действительно испортило возвращенные результаты. Единственная ситуация, в которой вы получите правильный результат, - это когда:

  • все соединения от основного объекта и объекта targetBy "target" являются взаимно однозначными;
  • все соединения - INNER JOIN;
  • столбец indexBy набора результатов уникален.

В каждом другом случае вы теряете некоторую ссылку на экземпляр во время гидратации.

В вашем примере вы используете LEFT JOIN: что произойдет со всеми объектами без продукта? Все отброшены, поэтому LEFT JOIN будет работать как INNER JOIN. Кроме того, ваше обходное решение предполагает, что вы хотите объединить объекты с одним и тем же индексом, но это не так, как работает Доктрина: в Доктрине столбец indexBy ДОЛЖЕН быть уникальным. Для получения дополнительной информации см. официальные документы.

Обратите внимание, что вы можете индексировать B p.id в свой JOIN, но это имеет другое значение. Это означает, что при увлажнении экземпляров UserIngredients сбор продукта должен быть проиндексирован идентификатором.

Итак, мое предложение состоит в том, чтобы следовать одному из этих двух решений:

  • отказаться от Doctrine indexBy и использовать ваш workaraound;
  • Если у вас есть обратная ассоциация Product -> UserIngredients, используйте Product в качестве основной сущности запроса (предложение from и сначала для выбора в select), а затем индексируйте p.id.

Какой из них использовать зависит от вашей бизнес-логики, но я обычно предпочитаю второе решение, так как первые генерируют многоуровневый массив, которые лучше представлены отношениями сущностей.

Надеюсь, что эта помощь!:)