Query builder "addSelect" affects "addOrderBy" - symfony

I have Symfony 4 width Doctrine, two entities/tables with relation Fixture (one) - Picture (many). Both tables has column with name "order".
I am loading (eagerly) all fixtures and its pictures. I am trying to sort query result only by root entity.
It's working as expected when not using "addSelect" function.
$qb = $repository->createQueryBuilder('fixture');
$qb->innerJoin('fixture.pictures', 'pictures');
$qb->addOrderBy('fixture.order', 'ASC');
The result is:
SELECT f0_.name AS name_0, f0_.color AS color_1, f0_.`order` AS order_2, f0_.id AS id_3, f0_.valid_from AS valid_from_4, f0_.valid_to AS valid_to_5, f0_.disabled AS disabled_6 FROM fixture f0_ INNER JOIN fixture_picture f1_ ON f0_.id = f1_.fixture_id ORDER BY f0_.`order` ASC
But when I change it to
$qb = $repository->createQueryBuilder('fixture');
$qb->innerJoin('fixture.pictures', 'pictures');
$qb->addSelect('pictures'); //eager loading
$qb->addOrderBy('fixture.order', 'ASC');
I am getting:
SELECT f0_.name AS name_0, f0_.color AS color_1, f0_.`order` AS order_2, f0_.id AS id_3, f0_.valid_from AS valid_from_4, f0_.valid_to AS valid_to_5, f0_.disabled AS disabled_6, f1_.id AS id_7, f1_.`order` AS order_8, f1_.file_name AS file_name_9, f1_.file_path AS file_path_10, f1_.fixture_id AS fixture_id_11 FROM fixture f0_ INNER JOIN fixture_picture f1_ ON f0_.id = f1_.fixture_id ORDER BY f0_.`order` ASC, f1_.`order` ASC
I am really not sure why is there "f1_.order ASC". I also tried it with
$qb->addOrderBy('pictures.order', 'ASC');
and for this case it's working (it's sorted only by pictures).
TL;DR;
When using "$qb->addSelect('pictures');" in query builder, extra order by ", f1_.order ASC" is added in SQL query even only root entity should be sorted. It's working when "addSelect" is not used or when query is sorted by joined table 'pictures.order'

#ehymel was right. I had #ORM\OrderBy annotation used in Entity "Fixture" and forgot about it.

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.

How to select specific properties of properties through DQL

I have the following Entities. First and foremost: the design derived from a legacy DB and it has been semplified here for clarity shake.
What I would like to do is selecting all the widgets along with their varsSelection populated (in this very moment Widget only contains PhyVarSelection instances so we can focus on them) which should have teir phyVar hydrated. Phyvar must not be populated with its ewCfgVar property.
I'm trying to do it by using DQL. I succeeded to select all the widgets and their relative varsSelection, but I'm not able to populate their relative phyVar. Is that possible? Here is the DQL I'm using:
$sql = <<<EOS
SELECT wid, partial phyvarsel.{id, phyVar, start}
FROM Belka\\TsBundle\\Entity\\Widget wid
LEFT JOIN Belka\\TsBundle\\Entity\\PhyVarSelection phyvarsel WITH wid.id = phyvarsel.widget
LEFT JOIN Belka\\TsBundle\\Entity\\PhyVar phyvar WITH phyvarsel.phyVar = phyvar.id
EOS;
$query = $this->getEntityManager()->createQuery($sql);
If I dump the result, Widget and varsSelection are correctly populated, but PhyVarSelection::phyVar is set to NULL.
The query DQL it generates is actually correct, and if I query it I get all the PhyVar's properties:
die(var_dump($query->getSQL()));
It generates:
SELECT a0_.id AS id0, a0_.title AS title1, a0_."order" AS order2, a0_.span_cols AS span_cols3, a0_.description AS description4, a1_.id AS id5, a1_.start AS start6, a2_.id AS id7, a0_.part_of_section AS part_of_section8, a1_.vartype AS vartype9, a1_.part_of_widget AS part_of_widget10, a1_.phy_var_sel AS phy_var_sel11, a2_.vartype AS vartype12, a2_.part_of_phy_meter AS part_of_phy_meter13, a2_.varname AS varname14, a2_.id_device AS id_device15 FROM app_t.widget a0_ LEFT JOIN app_t.var_selection a1_ ON (a0_.id = a1_.part_of_widget) AND a1_.vartype IN ('phy') LEFT JOIN app_t.variable a2_ ON (a1_.phy_var_sel = a2_.id) AND a2_.vartype IN ('phy');
I don't paste here the Entities' code but if you need it let me know with a comment below and I will update the question.
Update
I've also tried the following:
$sql2 = <<<EOS
SELECT phyvarsel, phyvar
FROM Belka\\TsBundle\\Entity\\PhyVarSelection phyvarsel
LEFT JOIN Belka\\TsBundle\\Entity\\PhyVar phyvar
WHERE phyvarsel.id = :phyvarselid
EOS;
$query2 = $this->getEntityManager()->createQuery($sql2);
$query2->setParameter('phyvarselid', '0');
$query2->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
$varSel = $query2->getResult();
again, in this case I get PhyVarSelection but its attribute phyVar is still set to NULL.

Doctrine query builder - nested queries, AS keyword, select functions

SELECT t1.field1 AS foo, (SELECT ABS(SUM(amount)) FROM table2 t2) FROM table1;
How can I convert this query into symfony 2 doctrine query builder?
$results = $this->getEntityManager()->createQueryBuilder()
->select(...)
//...
$results = $this->getEntityManager()->createQueryBuilder()
->select('t1.field1')
->addSelect('abs(sum(amount)) t2')
->from('BundleName:Entity', 't1)
->leftJoin('t1.field', 'another')
->getQuery()->getResult();
Maybe this works, I found a similar problem and a solution here:
Select Subquery with COUNT() in Doctrine DQL
Or if it not works, you can create two queries for the problem, and you use the first query's result in the second query.

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