Doctrine 2 DQL - comment sélectionner le côté inverse d'une requête unidirectionnelle de type "many-to-many"?

j'ai deux classes-Page et SiteVersion, qui ont beaucoup à beaucoup de relations. Seul SiteVersion est conscient de la relation (parce que le site est modulaire et je veux pouvoir emporter et déposer dans le module qui appartient à SiteVersion).

Comment choisir les pages en fonction des critères de SiteVersion?

Par exemple, cela ne fonctionne pas:

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

j'obtiens l'erreur:

[DoctrineORMQueryQueryException]
[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.

Même si je peux sélectionner "v" avec cette requête.

je pense que je pourrais peut-être résoudre cela en introduisant une classe pour la relation (une classe de PageToVersion), mais y a-t-il un moyen de ne pas le faire, ou de le rendre bidirectionnel?

18
demandé sur Gnuffo1 2011-03-25 15:28:16

5 réponses

il y a deux façons de traiter cela dans la Doctrine ORM. Le plus typique est l'utilisation d'un IN condition avec une sous-requête:

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
    )

L'autre façon est avec une jointure supplémentaire avec le fonctionnalité de jointure arbitraire introduite dans la version 2.3 de L'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 est juste à cause d'une limitation du courant de l'analyseur.

Veuillez noter que la limitation qui provoque l'erreur sémantique est parce que l' processus d'hydratation commence à partir de la racine des entités sélectionnées. Sans racine en place, l'hydrateur n'a aucune référence sur la façon de s'effondrer extraction de joint ou joint results.

24
répondu Ocramius 2013-03-16 01:53:32

je pense que vous devez sélectionner le SiteVersion dans votre requête:

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

Vous obtiendrez un tableau D'entités SiteVersion que vous pouvez boucler pour obtenir les entités de la Page.

2
répondu rojoca 2011-03-25 17:08:39

Je n'ai pas pu trouver comment faire fonctionner les requêtes natives, donc j'ai résolu d'une manière un peu hacky:

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

Je ne l'aime pas parce qu'il en résulte plus de requêtes à la base de données (surtout si je dois récupérer un tableau de pages au lieu d'une) mais il fonctionne.

Modifier: j'ai décidé de suivre un cours pour l'association. Maintenant, je peux faire cette requête:

SELECT p FROM Page p, SiteVersionPageLink l
WHERE l.page = p AND l.siteVersion = 5 AND p.slug = 'index'
1
répondu Gnuffo1 2011-03-28 12:47:22

Essayez ceci (ou quelque chose comme ça):

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

Je ne l'ai pas testé exactement, mais j'ai obtenu quelque chose de similaire à travailler. L'utilisation de EXISTS et MEMBER OF sont enterrés dans le DQL choisir des Exemples section du chapitre de la DQL.

1
répondu Ian Phillips 2013-03-15 06:50:13

j'ai trouvé une solution pour ce problème ici.

d'Après cette page, votre requête devrait ressembler à quelque chose comme ceci:

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

est-ce que ça résout votre problème?

0
répondu Imi Borbas 2011-03-25 12:42:22