Doctrine 2 DQL - how to select inverse side of unidirectional many-to-many query?

前端 未结 5 1116
南笙
南笙 2020-12-23 21:53

I have two classes - Page and SiteVersion, which have a many to many relationship. Only SiteVersion is aware of the relationship (because the site is modular and I want to b

相关标签:
5条回答
  • 2020-12-23 22:18

    I couldn't figure out how to get native queries working, so have resolved in a slightly hacky way:

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

    I don't like it because it results in more queries to the database (especially if I need to fetch an array of pages instead of one) but it works.

    Edit: I've decided to just go with a class for the association. Now I can do this query:

    SELECT p FROM Page p, SiteVersionPageLink l
    WHERE l.page = p AND l.siteVersion = 5 AND p.slug = 'index'
    
    0 讨论(0)
  • 2020-12-23 22:19

    I think you need to select the SiteVersion in your query too:

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

    You will get an array of SiteVersion entities which you can loop through to get the Page entities.

    0 讨论(0)
  • 2020-12-23 22:28

    I've found a possible solution for this problem here.

    According to that page, your query should look something like this:

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

    Does it solve your problem?

    0 讨论(0)
  • 2020-12-23 22:29

    There's two ways of handling this in Doctrine ORM. The most typical one is using an IN condition with a subquery:

    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
        )
    

    The other way is with an additional join with the arbitrary join functionality introduced in version 2.3 of the 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
    

    The 1 = 1 is just because of a current limitation of the parser.

    Please note that the limitation that causes the semantical error is because the hydration process starts from the root of the selected entities. Without a root in place, the hydrator has no reference on how to collapse fetch-joined or joined results.

    0 讨论(0)
  • 2020-12-23 22:29

    Try this (or something like it):

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

    I haven't tested this exactly, but I have gotten something similar to work. The use of EXISTS and MEMBER OF are buried in the DQL Select Examples section of the DQL chapter.

    0 讨论(0)
提交回复
热议问题