How to select specific properties of properties through DQL - symfony

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.

Related

Doctrine class map inheritance and join

I have 2 entities (ProductX and ProductY), herited from Product (inheritance type: JOINED, with Discriminator).
I need to add a condition on ProductX or productY field.
But the query builder doesn't show this entities.
I try :$queryBuilder->leftJoin('App\Entity\ProductX', 'productX', 'WITH', 'o.id = productX.id');
But in this case, the generated DQL is :
LEFT JOIN (
product_x p8_
INNER JOIN product p9_ ON p8_.id = p9_.id
) ON (p0_.id = p9_.id)
instead of :
LEFT JOIN product_x p8_ ON p0_.id = p8_.id
Any idea to do that ?
you can just do something like that I think :
$queryBuilder->leftJoin('App\Entity\ProductX', 'productX');
This will return something you want, other arguments it's generaly use for custom relationship
More detail here:
https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/query-builder.html
I hope this can help you

Query builder "addSelect" affects "addOrderBy"

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.

selecting only max clause without group by properties in subquery using Nhibernate

I have SQL query like this:
select * from dbo.table1 where Id in
(
select max(id) as id from dbo.table1 group by prop1, prop2, prop3
)
I want to create NHibernate query which is be able to do this for me. I tried to use QueryOver but it doesn't work. Do you have any suggestions how to do it?
NHibernate supports even this kind of queries. Please, see more in documentation: 15.8. Detached queries and subqueries. We just have to split the query (as in your SQL snippet) into two parts:
inner select
the select with the IN clause
Let's assume, that the dbo.table1 in the Questin is mapped into MyEntity.
To create inner select, let's use the DetachedCriteria
EDIT (extended with the Group by, SqlGroupProjection)
There is an extract of the SqlGroupProjection method:
A grouping SQL projection, specifying both select clause and group by
clause fragments
// inner select
DetachedCriteria innerSelect = DetachedCriteria
.For(typeof(MyEntity))
.SetProjection(
Projections.ProjectionList()
.Add(
Projections.SqlGroupProjection(
" MAX(ID) ", // SELECT ... max(ID) only
" Prop1, Prop2, Prop3", // GROUP BY ... property1, p2...
new string[] {"ID"}, // could be empty, while not used for
new IType[] { NHibernate.NHibernateUtil.Int32 } // transformation
)
)
;
Note: I've provided even the last two paramters, but in this case they could be empty: new string[], new IType[] {}. These are used only for Transformation (materialization from data into entity). And this is not the case, we are just building inner select...
// the select with IN clause
var result = session.CreateCriteria(typeof(MyEntity))
.Add(Subqueries.PropertyIn("ID", innerSelect))
.List<MyEntity>();
Also related could be 15.7. Projections, aggregation and grouping

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.

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