Symfony 2 query builder join without relation (cross join) - symfony

I am trying to use the query builder to join 2 tables which have no relation.
Desired end result:
SELECT x, y
FROM x
JOIN y
Query builder code:
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('x');
$qb->from('Test1', 'x');
$qb->join('Test2', 'y');
$qb->orderBy('x.name', 'ASC');
Produces the following DQL:
SELECT x FROM Test1 x INNER JOIN Test2 y ORDER BY x.name ASC
Which results in a syntax error:
[Syntax Error] line 0, col 137: Error: Expected Literal, got 'BY'
The entities Test1 and Test2 don't have a relation (not in the code, nor in the database).
Is there any way to accomplish this?
I would like to use the query builder, because I have a lot of other functionality for the query that depends on the query builder (for filtering and sorting etc.).
I know this is possible with plain SQL, or DQL queries (not produced by the query builder).

You can try the following possibility:
public function getYourData($users) {
$qb = $this->entityManager->createQueryBuilder();
$qb
->select('x', 'y')
->from('Test1', 'x')
->leftJoin(
'Test2',
'y',
\Doctrine\ORM\Query\Expr\Join::WITH,
'x.id = y.reference_id'
)
->orderBy('x.name', 'ASC');
return $qb->getQuery()->getResult();
}

Related

Join a subquery with Doctrine DQL

Using Doctrine in Symfony2, I need to recover each items and for each of them, the latest timestamp of their report.
So I would like to execute a query using DQL, which would be like this in SQL:
SELECT * from `item` i
LEFT JOIN `kit` k ON k.`id` = i.`kit_id`
LEFT JOIN
(SELECT e.`item_id`, MAX(e.`dateCreation`)
FROM `entete_rapport` e
GROUP BY e.`item_id`) latest ON latest.`item_id` = i.`id`
I am not able to have the same with DQL. I guess I have to separate the subquery et the main one, with something like this:
$subSelect->select('e AS ItemId, MAX(e.dateCreation) AS latest')
->from('CATUParkBundle:EnteteRapport', 'e')
->groupBy('e.item');
$qb->select('i')
->from('CATUParkBundle:Item', 'i')
->leftJoin('i.kit', 'k')
->leftJoin('CATUParkBundle:EnteteRapport f', sprintf('(%s)', $subSelect->getDQL()), 'latest', 'f.id = latest.ItemId');
I am not able to make this query work, I really need you guys.
Thank you in advance, you're awesome!
Seems like subqueries in joins do not work in dql. (I get error message: [Semantical Error] line 0, col 52 near '(SELECT e': Error: Class '(' is not defined.)
You could run $em->getConnection()->prepare($yourRawSql)->execute()->fetchAll(), but this returns a raw result array (no objects). See raw sql queries on knp-lab
Or do it in dql with HAVING instead of a subquery join:
$qb->select('i', 'i')
->addSelect('MAX(e.dateCreation)', 'date')
->from('CATUParkBundle:Item', 'i')
->leftJoin('i.kit', 'k')
->leftJoin('i.rapportOrWhatEver', 'f')
->groupBy('e.id');

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: ordering By Count on Associated entities

Goal: Ordering my entities on how ofter they are used on associated relations (oneToMany)
The problem: DQL, it somehow handles the FROM keyword is an entity or class.
The Exception thrown: QueryException: [Semantical Error] line 0 col 103 near 'FROM MyBundle:Entity Error: Class 'FROM' is not defined.
Here is a SQL query I wrote to test how to get the data. It works perfectly
SELECT en.id, (COUNT(DISTINCT ag.artist_id) + COUNT(DISTINCT rg.release_id) + COUNT(DISTINCT tg.track_id)) AS total
FROM myapp.entity AS en
LEFT JOIN myapp.other_one AS o1 ON o1.entity_id = en.id
LEFT JOIN myapp.other_two AS o2 ON o2.entity_id = en.id
GROUP BY en.id
ORDER BY total DESC ;
To get the data in symfony to hydrate to objects I try to use Doctrine Query Language in the EntityRepository like this:
/**
* Find Most Common Entities
*
* #return array
*/
public function findMostCommon()
{
$em = $this->getEntityManager();
$qb = $em->createQueryBuilder();
$qb
->select('en, (COUNT(DISTINCT en.other1) + COUNT(DISTINCT en.other2)) AS count')
->from('MarulaBundle:Entity', 'en')
->leftJoin('MyBundle:Other1', 'o1', 'WITH', 'o1.entity = en.id')
->leftJoin('MyBundle:Other2', 'o2', 'WITH', 'o2.entity = en.id')
->groupBy('en')
->orderBy('count', 'DESC')
;
return $qb->getQuery()->getResult();
}
Since it is possible in a SQL query. I was hoping it would work just as fine with DQL.
Has anyone experienced this error before? Is it even possible to achieve this with Doctrine, or is doctrine limited relating this issue?
OOPS: I should not use the keyword "count"
Solution:
->select('en, (COUNT(DISTINCT en.other1) + COUNT(DISTINCT en.other2)) AS HIDDEN orderCount')
note: adding the HIDDEN keyword helps to get only the entities back, which makes the hydration fit right in

Optimizing doctrine query using DQL

I use in my code a simple query that works fine in all the cases. This means that if the user has not a photo (for example) the query goes fine:
$user=$em->getRepository('UserBundle:User')->findOneById($id_user);
The entity User has a lot of relations with other entities, this is the reason to optimize the number of queries to avoid Doctrine's lazy loading. Then I make this query with DQL using QueryBuilder:
public function findUsuario($id_user){
$em = $this->getEntityManager();
$qb = $em->createQueryBuilder();
$qb->select('u, p, co, ci, f, s, q, b, r, se, com, t, beb, prof, v, h, i, idi, usf')
->from('UserBundle:User', 'u')
->innerJoin("u.pais", 'p')
->innerJoin("u.comunidad", 'co')
->innerJoin("u.ciudad", 'ci')
->innerJoin("u.fotos", 'f')
->innerJoin("u.sexo", 's')
->innerJoin("u.quiero", 'q')
->innerJoin("u.busco", 'b')
->innerJoin("u.relacionPareja", 'r')
->innerJoin("u.sexualidad", 'se')
->innerJoin("u.complexion", 'com')
->innerJoin("u.tabaco", 't')
->innerJoin("u.bebida", 'beb')
->innerJoin("u.profesion", 'prof')
->innerJoin("u.vivienda", 'v')
->innerJoin("u.hijo", 'h')
->innerJoin("u.ingreso", 'i')
->innerJoin("u.idiomas", 'idi')
->innerJoin("u.usuarioFavoritos", 'usf')
->where('u.id = :id')
->setParameter('id', $id_user);
$query= $qb->getQuery();
return $query->getSingleResult();
}
This works well when the user has information on all related entities, but if for example a user has no photo, the following exception occurs: "No result was found for query although at least one row was expected"
I don't understand why, can anyone shed some light? Thanks
An inner join should be used when you want all your rows to have data on the other side of the relationship, if no data exists on the other side of the relationship then the row will be omitted.
You can find more details about the different type of JOINs on this page.
Instead of the innerJoin method you will need to use the leftJoin method.
(Also, you have a bigger problem: you have way too many JOINs, I'd advise you to review the organization of your entities.)

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