Doctrine2 QueryBuilder subquery with SUM? - symfony

I'm trying to get Hotels from my database that have enough beds (user specifies guest count parameter). The query should look like this:
SELECT h.* FROM Hotel AS h
WHERE
(SELECT SUM(r.guestCount * r.count)
FROM Room AS r
WHERE r.hotel_id = h.id) >= $questCount
The above query contains subquery in the where clause. I've read doctrine's QueryBuilder documentation and I don't know how to make subquery in QB.
All I have now is:
$qb = $this->getDoctrine()->getEntityManager()->createQueryBuilder()
->select('h')
->from('AAAHotelsBundle:Hotel', 'h')
->where(.........???...........);
Any ideas what to do next?
Of course I simplified the problem (the query itself is much more complex). I use Symfony2.

DQL is not a solution in my case. I do really need to use QueryBuilder.
I've asked the same question on Google Groups and here is the solution:
$qb = $this->getDoctrine()->getEntityManager()->createQueryBuilder()
->select('h')
->from('AAAHotelsBundle:Hotel', 'h')
->join('Room', 'r')
->groupBy('h')
->having('SUM(r.guestCount * r.count) >= :guestCount')
->setParameter("guestCount", $guestCount);

I think this DQL will help you
SELECT h, SUM(r.guestCount * r.count) as TSUM FROM Hotel h JOIN h.room r
WHERE TSUM >= :questCount

Related

Use the Doctrine's QueryBuilder to get Categories with a minimum number of products

I want to use the Doctrine's QueryBuilder to get Categories with a specified attribute and a number minimum of products inside. I precise that's the first time I use this Doctrine's function. I discovered that and I realized that it's so much powerful than basics repositories functions. I'm a very beginner with SQL because I used to use Repository's functions.
But I think I achieve this in pure SQL:
SELECT category.*,COUNT(*)
FROM category_product
INNER JOIN category ON category_product.category_id = category.id
WHERE category.name = 'region'
GROUP BY category_product.category_id HAVING COUNT(*) > 20
At the moment I'm totally lost with DQL construction. My Category and Product Entities both have a Many to Many relationship and I can't reach traducing this to Doctrine. I tried using the category_product table auto-generated by doctrine's but It doesn't want to access this table...
$qb = $this->createQueryBuilder('c');
$qb
->select('c')
->where("c.name = 'region'")
->innerJoin('c.products', 'p', 'WITH', 'COUNT(c.products) > :minimum')
->setParameter('minimum', $minimum);
dump($qb->getQuery(), $qb->getQuery()->getResult());
Each Time I uses a new construction I have Semantical errors I'm not able to correct...
Thanks a lot for your help
You could try some thing like here (piece of code from my working model)
$qb = $this->createQueryBuilder('c')
->select('c')
->where('c.name = region')
->addSelect('COUNT(c.products) AS counter')
->innerJoin('c.products', 'p')
->groupby('p.id')
->having('count(p.id) >= :minimum')
->setParameter('region', $region)
->setParameter('minimum', $minimum)
;
dump($qb->getQuery()->getResult());
related post: here
Ok Thanks to you I got it.
I shouldn't select the COUNT because I typed the return to be an Array of categories but it returned an array of arrays with contains the category and the count... I had to group by c too.
Thanks a lot for your help !
The answer is :
$qb = $this->createQueryBuilder('c')
->select('c')
->where('c.name = :region')
->innerJoin('c.products', 'p')
->groupBy('c')
->having('SIZE(c.products) > :minimum')
->setParameter('region', $region)
->setParameter('minimum', 20);

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.

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

Doctrine2 - need some help modifying working dql

I have the following dql SELECT query, which works:
SELECT q AS question, AVG(r.score) AS average
FROM MyBundle:Question q
JOIN q.ratings r
WHERE q.deleted IS NULL
GROUP BY q.id
ORDER BY q.id ASC
What I'd like to do is make it return 0 if AVG(r.score) is either 0 or null. I'm just not sure how to do it. Is it possible to do something like:
SELECT q AS question, (AVG(r.score) OR 0) AS average FROM....
Since you can't use aggregate function in a WHERE clause, you must use HAVING instead. That should do the trick:
SELECT q AS question, AVG(r.score) AS average
FROM MyBundle:Question q
JOIN q.ratings r
WHERE q.deleted IS NULL
GROUP BY q.id
HAVING average IN (0, NULL)
ORDER BY q.id ASC
Are you looking for an alternative of mysql IFNULL to a doctrine query builder. Look at this question.
Changing my (INNER) JOIN to a LEFT OUTER JOIN gave me the results I was looking for.

symfony2 doctrine join

Okay, so i've got a query that i've researched and researched how to get this to work and for the life of me i cant!... perhaps i'm just doing this incorrectly and the minimal information ive found..
I've got a table named timeclock setup.. which has a field: noteBy_id in it which is an id to the user the record belongs to...
What I need to do now, is for the management side of things in the system.. I anticipate more than 1 company using this timeclock system, and as such I need to filter the results based on the company id.. In the user table, i have a field named parentcompany_id
So, lets see if I can express in words what I need to do..
I need to Select * from timeclock and left join user.parentcompany_id where timeclock.daydate < :start and where u.parentcompany = :pid
where start is: `date('Y-m-d 00:00:00');
I've setup this query:
$em = $this->getDoctrine()->getEntityManager();
$start = date('Y-m-d 00:00:00');
$qb = $em->getRepository('EcsCrmBundle:TimeClock');
$qb = $qb->createQueryBuilder('t');
$query = $qb->select('t, u.parentcompany_id')
->from('timeclock', 't')
->leftJoin('Ecs\AgentManagerBundle\Entity\User', 'u', 'ON' 'u.id = t.noteBy_id AND u.parentcompany_id = :pid')
->where('t.daydate < :start')
->andWhere("t.noteBy_id != ''")
->setParameter('start', $start)
->setParameter('pid', $user->getParentcompany())
->getQuery();
$entities = $query->getArrayResult();
I've looked and looked and can't find a solution to the error that I get which is:
An exception has been thrown during the rendering of a template ("[Semantical Error] line 0, col 112 near 'u ON u.id = t.noteBy_id': Error: Identification Variable Ecs\AgentManagerBundle\Entity\User used in join path expression but was not defined before.") in EcsCrmBundle:TimeClock:manager.html.twig at line 5.
and the query that gets output is:
SELECT t, u.parentcompany_id FROM Ecs\CrmBundle\Entity\TimeClock t LEFT JOIN Ecs\AgentManagerBundle\Entity\User u ON u.id = t.noteBy_id AND u.parentcompany_id = :pid, timeclock t LEFT JOIN Ecs\AgentManagerBundle\Entity\User u ON u.id = t.noteBy_id AND u.parentcompany_id = :pid WHERE t.daydate < :start AND t.noteBy_id != ''
which under normal circumstances would work perfectly... but in this, it just doesn't... Any ideas?
I've recently had to do it like this.. I'm guessing in this that your noteBy is a ManyToOne in the user table and you are wanting to have it filter the results by the company of the admin that is currently logged into your system..
So, adapting a join I had to write myself for such a task is easy enough. I personally like to use the QueryBuilder so this will be done in query builder..
Your first mistake in your query is the ->from('timeclock', 't') line. Because you have previously created your object with $qb = $em->getRepository('EcsCrmBundle:TimeClock'); $qb = $qb->createQueryBuilder('t'); you don't need the from in the query builder, as it will be generated for you.
The next issue, is the leftJoin and I'll explain why when I've shown you a working version.
And the last issue, preventing this from working how you want it - is a missing andWhere clause. So, lets take a look at a working query.
$query = $qb->select('t, u')
->leftJoin('t.noteBy', 'u', 'WITH', 'u.id = t.noteBy')
->where('t.daydate < :start')
->andWhere('u.parentcompany = :pid')
->setParameter('start', $start)
->setParameter('pid', $user->getParentcompany())
->getQuery();
So because we've already created the object by using $qb = $qb->createQueryBuilder('t') we just select t and u
For the join, we're joining the timeclock table by the noteBy column, which is the user id from the user table. So, the first argument being the "from" alias. So, since we've aliased the timeclock table with t we use t.noteBy. The next argument in the leftjoin is the alias of the 2nd table, which is u in this case but can be anything.. The third argument for a leftJoin anyway - is the way you join it.. either a WITH or ON will work here. and the 4th argument, is the match you wish it to have.. in this case u.id must equal t.noteBy
You will see that I got rid of one of the andWhere, I did this because with the properly structured query you shouldn't need it. I did however add in the andWhere for the u.parentcompany since that is afterall what you are looking to filter by you should have it in a WHERE instead of as a match in the join itself.
The documentation is very limited in this, and it took me a while to figure it all out as well.. You, undoubtedly - like me, came to using doctrine from writing your queries by hand. And So since you seem to be just starting with Symfony (i am myself as well about 2 months in now), you're still in the hand-coding mindset. But with further time, you'll start understanding the DQL way of life. Try this query out and see what happens.
Ok, first you would need to relate entity Timeclock to Company. Whenever you want to join two entities in Doctrine they need to be related by some attribute (that is, table column).
I don't see any need for User entity in this query as all info is available through Company entity and you are not filtering down results based on any user properties.
You desired query should look something like this (more or less). I took liberty and ditched _id suffixes from entity attributes as they tend to cloud what is really going on. ;)
$query = $this->getEntityManager()->createQuery("SELECT t, c.id FROM EcsCrmBundle:TimeClock t JOIN t.company c WHERE c.id = :pid AND t.daydate < :start AND t.noteBy != ''");
$query->setParameter('start', $start);
$query->setParameter('pid', $user->getParentcompany());
return $query->getArrayResult();
Also, I did inner-join (JOIN) as I think there could not be timeclock without it's company but feel free to change that to LEFT JOIN if that suits you better.
Is this what you were trying to achieve?

Resources