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?
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.
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.
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'
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.
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?