Doctrine left join on unrelated entity - symfony

Is there any way to do this. I'm getting more and more confused trying different things.
I have an entity conferences that can have a place.
Places are in a many to one relationship with city.
In my query I'm trying to retrieve the city info but can't seem to retrieve it in the same place result.
This is the query used:
$qbt = $this->_em->createQueryBuilder();
$qbt
->select('conference', 'diffusion', 'speaker', 'placediff', 'confcity')
->from('AppBundle:Conference', 'conference')
->leftJoin('conference.diffusion', 'diffusion')
->leftJoin('conference.speaker','speaker')
->leftJoin('conference.place','placediff')
->leftJoin('AppBundle:City', 'confcity', 'WITH', 'confcity.id = placediff.city');
return $qbt
->getQuery()
->setHint(\Doctrine\ORM\Query::HINT_INCLUDE_META_COLUMNS, true)
->useQueryCache(true)
->useResultCache(true, 3600)
->getArrayResult();
This is what is returns, currently have only one conference.
Would love to have the second array inside place though.
Any ideas on how to accomplish this? Much obliged ~
(UPDATE) In the meantime I switched to raw sql query but really looking for a way to do this using dql
public function rawConf()
{
$conn = $this->getEntityManager()->getConnection();
$sql = 'SELECT
c0_.id AS id,
c0_.startAt AS startat,
c0_.comment AS comment,
d1_.id AS diffusion_id,
d1_.hour AS diffusion_hour,
s2_.id AS speaker_id,
c0_.place_id AS place_id,
c0_.sponsor_id AS sponsor_id,
c0_.tour_id AS tour_id_8,
d1_.movie_id AS diffusion_movie_id,
s2_.contact_id AS speaker_contact_id,
c6_.name AS ville_name,
c6_.postal AS ville_post,
c6_.department as ville_depart
FROM
conference c0_
LEFT JOIN conference_diffusion c3_ ON c0_.id = c3_.conference_id
LEFT JOIN diffusion d1_ ON d1_.id = c3_.diffusion_id
LEFT JOIN conference_speaker c4_ ON c0_.id = c4_.conference_id
LEFT JOIN speaker s2_ ON s2_.id = c4_.speaker_id
LEFT JOIN place p5_ ON c0_.place_id = p5_.id
LEFT JOIN city c6_ ON (c6_.id = p5_.city_id)';
$stmt = $conn->prepare($sql);
$stmt->execute();
return $stmt->fetchAll();
}

You should get "city" from "place" as "$city" is a property of Place entity. Try this simplified query to see it works:
$qbt = $this->_em->createQueryBuilder();
$qbt
->select('conference')
->addSelect('place')
->addSelect('city')
->from('AppBundle:Conference', 'conference')
->innerJoin('conference.place', 'place')
->innerJoin('place.city', 'city')
;
And if it works - you could easily add more joins if you need.

Related

Doctrine querybuilder returns not all records in one to many relation with leftJoin

I have an Ad entity that represents an advertisement. This Ad entity has a one too many relation with adRemark. Reason to do this is that the adRemark contains multiple records because of multi language support.
An Ad can have one only one adRemark per language but can mis records for language that are not filled in or can even have no adRemark record as no language data is filled in.
I'm building a query that retrieves all ads including the adRemark.
$query = $this->createQueryBuilder('ad')
->select('ad.id, ad.title, ad.year, ad.hours, ad.status')
->addSelect('rem.remark')
->leftJoin('ad.remark', 'rem')
->andWhere("rem.language = 'NL' or rem.language is null")
->getQuery()
->getResult();
With this query i'm getting all ads that have for example the dutch (NL) remark filled in or no adRemark records. But i'm missing the ads the have for example no NL adRemaks record but do have an EN or DE record.
I working this for hours but i'm not able to define a good query. Help is really appreciated.
Herby the sql dump:
"SELECT ad.id, ad.title, ad.year, ad.hours, ad.status, rem.remark FROM
Mtr\Bundle\Entity\Ad LEFT JOIN ad.remark rem WHERE (rem.language = 'NL' or rem.language is null)"
You do not want to filter all the result set, but only on the join, so move the condition to the join clause.
Change the query to:
use Doctrine\ORM\Query\Expr\Join;
$query = $this->createQueryBuilder('ad')
->select('ad.id, ad.title, ad.year, ad.hours, ad.status')
->addSelect('rem.remark')
->leftJoin('ad.remark', 'rem', Join::WITH, "rem.language = 'NL' OR rem.language is null")
->getQuery()
->getResult();
References
SQL join: where clause vs. on clause
Why and when a LEFT JOIN with condition in WHERE clause is not equivalent to the same LEFT JOIN in ON?
How to do left join in Doctrine?

Convert SQL into Doctrine 2 QueryBuilder

I need help to convert a valid SQL query into createQueryBuilder. The problem I have is I don't know how to LEFT JOIN on a SELECT in the createQueryBuilder.
SELECT username, count(c.user_owner_id) as num_contact, a_g.name as
group_name
FROM `oro_user` as u
INNER JOIN `oro_user_access_group` as u_g on u.id=u_g.user_id
INNER JOIN `oro_access_group` as a_g on u_g.group_id=a_g.id
LEFT JOIN
(SELECT cc.user_owner_id
FROM `orocrm_contact` as cc
INNER JOIN`orocrm_contact_to_contact_grp` as cc_g on cc_g.contact_id=cc.id
INNER JOIN `orocrm_contact_group`
as c_g on cc_g.contact_group_id=c_g.id
WHERE c_g.label='New One' and cc.semester_contacted='2017A')
as c on u.id=c.user_owner_id
WHERE a_g.name='Full-timer' and u.enabled = 1 and u.gender='male'
GROUP BY u.id
ORDER BY num_contact
I have two queries below, I want user to LEFT JOIN the results from contact
$user = $this->em->getRepository('OroUserBundle:User')->createQueryBuilder('u')
->select('u.username')
->innerJoin('u.groups','g')
->andWhere('g.name = :group')
->setParameter('group', 'Full-timer')
->getQuery();
$contacts = $this->em->getRepository('OroContactBundle:Contact')->createQueryBuilder('c')
->select('c')
->innerJoin('c.groups','g')
->andWhere('g.label = :group')
->andWhere('c.semester_contacted = :sem')
->setParameter('group', 'New One')
->setParameter('sem', '2017A')
->setMaxResults(1)
->getQuery();
This is a pretty complex queries and since the ORM QueryBuilder works closer to your entities than the database I'm not sure if you can just "dump" the DQL into a ->join(). The good news with the DBAL QueryBuilder that works:
$dbalQueryBuilder
->from('user_table as u')
...
->join('u', '('.$otherDbalQueryBuilder->getSQL().')', 'c')
This is from memory so it might be a little different, but something like that.
With that you can get all the fields you require, but you won't get any entities. Luckily Doctrine provides ways to build entities from Native SQL using ResultSetMapping.
$userWithContacts = $entityManager->createNativeQuery(
$dbalQueryBuilder->getSQL(),
$yourResultSetMapping
);
I know this is will require more code and is probably not as nice as just using the ORM QueryBuilder, but I find it oftentimes to be the best way to deal with existing queries that need to be ported to ORM somehow.

Using left join with query builder doctrine

Having some problems retrieving ManytoOne relationships when using left join.
Before was using this query to query for conferences
$qb = $this->createQueryBuilder('u')
->select('u.id,u.comment,
IDENTITY(u.place) AS place_id,
IDENTITY(u.sponsor) AS sponsor_id,
IDENTITY(u.tour) AS tour_id,
u.startat
');
Now I'm trying to left join with diffusion which is tied to the diffusion in a many to many relationship.
$qbt = $this->createQueryBuilder('u')
->select('u','c')
->from('AppBundle:Conference', 'p')
->leftJoin('p.diffusion', 'c');
However this query doesn't return the u.place, u.sponsor and u.tour which are ManyToOne relationships.
leftJoin must be followed by 'WITH'. So for example:
->leftJoin('p.diffusion', 'p', 'WITH', 'p.user=u.id', 'u.id');
But i think it's better to post both your entities so i can give you the exact answer.
Found the issue , I had to add
->setHint(\Doctrine\ORM\Query::HINT_INCLUDE_META_COLUMNS, true)
to the getQuery , because getArrayResults by default doesn't return foreign keys( the place, sponsor and tour respectivly).
Here is my final query in the conference repository
$qbt = $this->_em->createQueryBuilder();
$qbt->select('conference','diffusion')
->from('AppBundle:Conference', 'conference')
->leftJoin('conference.diffusion', 'diffusion');
return $qbt
->getQuery()
->setHint(\Doctrine\ORM\Query::HINT_INCLUDE_META_COLUMNS, true)
->useQueryCache(true)
->useResultCache(true,3600)
->getArrayResult();

Doctrine ManyToMany Query

I have a Product Entity which has a ManyToMany relationship with a Taxon Entity. I want to find all the products that belong to the intersection of all Taxons. For instance I want to find all products that belong to taxons with IDs 1 and 2.
Products
{1,2,3}
Taxons
{1,2,3,4,5}
ProductsToTaxons
{{p1,t1},{p1,t2}, {p2,t2}}
I want to retrieve the following set ONLY when querying products for taxons 1 and 2:
Product
{1}
which is from {{p1,t1}, {p1,t2}}
Okay, So here is the DQL that i tried... but it doesn't work?
SELECT p FROM SRCProductBundle:Product p
JOIN p.taxons t
WHERE t.id = 1 AND t.id = 2
(P.S. I would also do this with QueryBuilder with as well)
EDIT
To clarify, here is the SQL that I would like to translate into DQL/QueryBuilder.
select p.id
from product p
where exists (select product_id
from product_to_taxon
where taxon_id = 1
and product_id = p.id)
and exists (select product_id
from product_to_taxon
where taxon_id = 4
and product_id = p.id);
You can use the MEMBER OF statement to achieve this although in my experience it hasn't produced very performant results with a ManyToMany relationship:
In DQL:
SELECT p FROM SRCProductBundle:Product p
WHERE 1 MEMBER OF p.taxons OR 2 MEMBER OF p.taxons
Or with Query builder
$this->createQueryBuilder('p')
->where(':taxon_ids MEMBER OF p.taxons')
->setParameter('taxon_ids', $taxonIdsArray)
->getQuery()
->getResult();
This will create SQL similar to the example provided although in my experience it still had a join in the EXISTS subqueries. Perhaps future versions of Doctrine can address this.
I think you want something like this:
$qb = $this
->createQueryBuilder('p')
->select('p.id')
;
$qb
->leftJoin('p.taxons', 'taxon1', Join::WITH, 'taxon1.id = :taxonId1')
->setParameter('taxonId1', 1)
->andWhere($qb->expr()->isNotNull('taxon1'))
->leftJoin('p.taxons', 'taxon2', Join::WITH, 'taxon2.id = :taxonId2')
->setParameter('taxonId2', 2)
->andWhere($qb->expr()->isNotNull('taxon2'))
;
Which is equivalent to the SQL:
SELECT p.id
FROM products p
LEFT JOIN taxons t1 ON (p.id = t1.product_id AND t1.id = 1)
LEFT JOIN taxons t2 ON (p.id = t2.product_id AND t2.id = 2)
WHERE t1.id IS NOT NULL
AND t2.id IS NOT NULL
;
Your DQL has wrong logic. You can't have a taxon with both id=1 and id=4. You could do it like this:
SELECT p FROM SRCProductBundle:Product p
JOIN p.taxons t
WHERE t.id = 1 OR t.id = 4
But I would prefer this way:
SELECT p FROM SRCProductBundle:Product p
JOIN p.taxons t
WHERE t.id IN (1, 4)
Using query builder that would look something like this, assuming you're in EntityRepository class:
$this->createQueryBuilder('p')
->join('p.taxons', 't')
->where('t.id IN :taxon_ids')
->setParameter('taxon_ids', $taxonIdsArray)
->getQuery()
->getResult();
For lack of a clean way to do this with DQL, and after a considerable amount of research, I resorted to doing this in Native SQL. Doctrine allows Native SQL via the EntityManager with createNativeQuery().
So in short, I utilized this ability and constructed the SQL query included in my question as a string and then passed it to the createNativeQuery() function.
This does appear to have some drawbacks as it appears I will be unable to use the KnpPaginatorBundle with it... So I might end up just filtering the results in PHP rather than SQL, which I'm hesitant to do as I think there are performance drawbacks.

Symfony2 Doctrine SQL object mapping on subselect

I'm trying to use this query:
MySQL SELECT DISTINCT by highest value
SELECT
p.*
FROM
product p
INNER JOIN
( SELECT
magazine, MAX(onSale) AS latest
FROM
product
GROUP BY
magazine
) AS groupedp
ON groupedp.magazine = p.magazine
AND groupedp.latest = p.onSale ;
Within Symfony2 and DQL.
I have:
$query = $em->createQuery("SELECT p FROM MyBundle:Product p WHERE p.type = 'magazine' AND p.maglink IS NOT NULL OR (p.type = 'magazine' AND p.diglink IS NOT NULL) GROUP BY p.magazine ORDER BY p.onSale DESC");
Which works fine with and outputs objects but without the correct MAX(onSale)
Doing:
$query = $em->createQuery("SELECT p , MAX(p.onSale) FROM MyBundle:Product p WHERE p.type = 'magazine' AND p.maglink IS NOT NULL OR (p.type = 'magazine' AND p.diglink IS NOT NULL) GROUP BY p.magazine ORDER BY p.onSale DESC");
Results in non-objects being returned.
This:
$query = $em->createQuery("SELECT
p.*
FROM
MyBundle:Product p
INNER JOIN
( SELECT
p.magazine, MAX(onSale) AS p.latest
FROM
MyBundle:Product p
GROUP BY
p.magazine
) AS groupedp
ON groupedp.magazine = p.magazine
AND groupedp.latest = p.onSale ;");
Throws this error:
[Semantical Error] line 0, col 127 near 'SELECT
': Error: Identification Variable ( used in join path expression but was not defined before.
I assume due to this Symfony2 Doctrine query
How can I maintain my mapping while still being able to sort each item by onsale?
Have you considered splitting this into two queries:
First:
Run your query directly against the database connection, and return the IDs of the relevant rows, in the order you want. This separates your complex query from DQL.
Second:
Query the rows through Doctrine to get the full entities you want, based on the IDs/orders of the previous query.
This is a shot in the dark, but it seems fairly obvious. Your aliasing two tables to the same alias. Thus when your using p. in the join it thinks your working from the original definition of p from before the join, then you alias the join as p. Change the alias of the join (and the references to that table) so each alias is unique.

Resources