Doctrine DQL, наследование таблицы классов и доступ к полям подкласса
У меня проблема с DQL-запросом и специализацией.
У меня есть Entity, называемый Auction
, который имеет отношение OneToOne
с Item
. Item
является mappedSuperclass
для Film
и Book
. Мне нужен запрос, который может помочь поисковой системе, позволяя пользователю искать аукционы с различными свойствами AND
, продавая товары с разными свойствами (это часть AND
, которая делает ее сложной).
Проблема в том, что даже если Auction
имеет ассоциацию, указывающую на Item
как таковую, мне нужно иметь доступ к Film
- и Book
-специфическим полям. Пользователи укажут тип Item
, который они ищут, но я не вижу способа использовать эту информацию, кроме использования INSTANCE OF
в моем DQL-запросе.
До сих пор я пробовал использовать такой запрос, как:
SELECT a FROM Entities\Auction a
INNER JOIN a.item i
INNER JOIN i.bookTypes b
WHERE i INSTANCE OF Entities\Book
AND b.type = 'Fantasy'
AND ...".
Такой запрос приводит к ошибке:
Класс Entities\Item
не имеет поля или ассоциации с именем bookTypes
что является ложным для Book
, но верно для Item
.
Я также пробовал
SELECT a FROM Entities\Book i
INNER JOIN i.auction a ...
но я считаю, что Doctrine требует, чтобы я ссылался на ту же Entity в операторах SELECT
и FROM
.
Если это важно, я использую наследование таблицы классов. Тем не менее, я не думаю, что переход на одно наследование таблицы может сделать трюк.
Любые идеи?
Ответы
Ответ 1
Как сказал Мэтт, это старая проблема, которую Doctrine Project не будет исправлять (DDC-16).
Проблема в том, что доктрина DQL - это статически типизированный язык, который имеет определенную сложность во внутренних функциях.
Мы думали о том, чтобы разрешить повышение рейтинга пару раз, но попытка получить эту работу просто не стоит того, и люди просто злоупотребляют синтаксисом, делая очень опасные вещи.
Как указано в DDC-16, также невозможно понять, к какому классу принадлежит свойство, не подвергая себя неприятным проблемам, таким как несколько подклассов, определяющих одни и те же свойства с разными именами столбцов.
Если вы хотите отфильтровать данные в подклассах в CTI или JTI, вы можете использовать технику, описанную мной в fooobar.com/questions/286873/.... Это связывает ваш DQL со всеми вовлеченными подклассами.
DQL, который вам понадобится в вашем случае, скорее всего (предполагается, что Entities\Book
является подклассом Entities\Item
):
SELECT
a
FROM
Entities\Auction a
INNER JOIN
a.item i
INNER JOIN
i.bookTypes b
WHERE
i.id IN (
SELECT
b.id
FROM
Entities\Book b
WHERE
b.type = 'Fantasy'
)
Это псевдокод для вашей проблемы. Это не приятно, но имейте в виду, что SQL и DQL очень разные и следуют различным правилам.
Ответ 2
Обновлено:
Я нашел решение для этого. См. Мой ответ на этот связанный вопрос:
Doctrine2: Полиморфные запросы: поиск по свойствам подклассов
Ответ 3
Команда Doctrine заявила, что они не собираются добавлять поддержку для этого:
http://www.doctrine-project.org/jira/browse/DDC-16
Соответствующие комментарии на этой странице:
Это действительно сложно. Однако такой синтаксис никогда не может работать, потому что может быть несколько подклассов, у которых есть поле с именем "d", поэтому Доктрина не знала, какое поле вы имеете в виду.
Я закрываю это.
Требование этой проблемы в основном нарушает принципы OO.
Если вам действительно нужно фильтровать несколько дочерних объектов в вашем наследование, затем попробуйте следующее:
SELECT r FROM Корень r ГДЕ r.id IN ( ВЫБРАТЬ c.id ИЗ Ребенок c ГДЕ c.field =: значение )
Ответ 4
Вы можете легко решить это, объединив свой базовый объект с вашим классом наследования с помощью id:
SELECT a FROM Entities\Auction a
INNER JOIN a.item i
INNER JOIN Entities\Book b WITH b.id = i.id
INNER JOIN b.bookTypes bt
WHERE bt.type = 'Fantasy'
AND...
или с помощью queryBuilder:
$queryBuilderb->select('a')
->from('Entities\Auction', 'a')
->innerJoin('a.item', 'i')
->innerJoin('Entities\Book', 'b', 'WITH', 'b.id = i.id')
->innerJoin('b.bookTypes', 'bt')
->where('bt.type = :type')
->andWhere(...
->setParameter('type', 'Fantasy');
Это основано на ответе Йан Филипса в вопросе здесь
Ответ 5
У меня была такая же проблема, и я не нашел решения без использования отдельных запросов для каждого подкласса и слияния их позже на уровне приложения.
Одно я уверен, одно наследование таблицы не решит это, совершенно одно и то же.
Существует еще одна альтернатива, хотя и логически грязная.
Определите все поля (те, которые вам нужны) в суперклассе. Если запись логически не имеет этого поля, она будет пустой. Не красивое зрелище, но эй, более оптимизированное, чем 2-3-4 -... запросы. Также в этом случае однонаправленное наследование таблицы, безусловно, лучший способ пойти