Подтвердить что ты не робот

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

Ответ 3

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

$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'

Ответ 4

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

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.

Ответ 5

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

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

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

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