Doctrine 2 DQL – как выбрать обратную сторону однонаправленного запроса «многие ко многим»?

У меня есть два класса – Страница и SiteVersion, которые имеют много-много отношений. Только SiteVersion знает об отношениях (потому что сайт является модульным, и я хочу убрать и отбросить модуль, к которому принадлежит SiteVersion).

Как я могу поэтому выбирать страницы на основе критериев SiteVersion?

Например, это не работает:

SELECT p FROM SiteVersion v JOIN v.pages p WHERE v.id = 5 AND p.slug='index' 

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

 [Doctrine\ORM\Query\QueryException] [Semantical Error] line 0, col -1 near 'SELECT p FROM': Error: Cannot select entity through identification variables without choosing at least one root entity alias. 

Хотя я могу выбрать «v» с этим запросом.

Я думаю, что я мог бы решить это, представив класс для отношений (класс PageToVersion), но есть ли какой-либо способ без этого или сделать его двунаправленным?

Related of "Doctrine 2 DQL – как выбрать обратную сторону однонаправленного запроса «многие ко многим»?"

В Doctrine ORM есть два способа обращения. Наиболее типичным является использование условия IN с подзапросом:

 SELECT p FROM SitePage p WHERE p.id IN( SELECT p2.id FROM SiteVersion v JOIN v.pages p2 WHERE v.id = :versionId AND p.slug = :slug ) 

Другой способ заключается в дополнительном объединении с произвольной функциональностью объединения, введенной в версии 2.3 ORM :

 SELECT p FROM SitePage p JOIN SiteVersion v WITH 1 = 1 JOIN v.pages p2 WHERE p.id = p2.id AND v.id = :versionId AND p2.slug = :slug 

1 = 1 происходит только из-за ограничения тока синтаксического анализатора.

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

Я думаю, вам нужно также выбрать SiteVersion в вашем запросе:

 SELECT v, p FROM SiteVersion v JOIN v.pages p WHERE v.id = 5 AND p.slug='index' 

Вы получите массив объектов SiteVersion, которые вы можете пропустить, чтобы получить объекты страницы.

Я не мог понять, как работать с родными запросами, поэтому решил немного взлохмоло:

 $id = $em->getConnection()->fetchColumn("SELECT pages.id FROM pages INNER JOIN siteversion_page ON siteversion_page.page_id = pages.id INNER JOIN siteversions ON siteversion_page.siteversion_id = siteversions.id WHERE siteversions.id = 1 AND pages.slug = 'index'"); $page = $em->find('Page', $id); 

Мне это не нравится, потому что это приводит к большему количеству запросов к базе данных (особенно если мне нужно получить массив страниц вместо одного), но он работает.

Изменить : я решил просто пойти с классом для ассоциации. Теперь я могу сделать этот запрос:

 SELECT p FROM Page p, SiteVersionPageLink l WHERE l.page = p AND l.siteVersion = 5 AND p.slug = 'index' 

Попробуйте это (или что-то вроде этого):

 SELECT p FROM Page p WHERE EXISTS (SELECT v FROM SiteVersion v WHERE p MEMBER OF v.pages AND v.id = 5 AND p.slug = 'index') 

Я не проверял это точно, но у меня что-то похожее на работу. Использование EXISTS и MEMBER OF похоронено в разделе DQL Select Examples главы DQL.

Я нашел возможное решение этой проблемы.

Согласно этой странице, ваш запрос должен выглядеть примерно так:

SELECT p FROM SiteVersion v, Page p WHERE v.id = 5 AND p.slug='index' AND v.page = p;

Решает ли ваша проблема?