Doctrine 2 DQL - как выбрать обратную сторону однонаправленного запроса «многие ко многим»? - PullRequest
19 голосов
/ 25 марта 2011

У меня есть два класса - Page и 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), но есть ли способ без этого или сделать его двунаправленным?

Ответы [ 5 ]

24 голосов
/ 16 марта 2013

Есть два способа справиться с этим в 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 только из-за текущего ограничения синтаксического анализатора.

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

2 голосов
/ 25 марта 2011

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

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

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

1 голос
/ 12 июля 2012

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

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 главы DQL.

1 голос
/ 25 марта 2011

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

$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'
0 голосов
/ 25 марта 2011

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

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

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

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

...