DQL join between unrelated entities? - doctrine-orm

Can I have a DQL join between unrelated entities using WITH DQL operator? OR is definign relationship absolutely mandatory?
I have a unidirectional relationship with Category and CategorySubsription. Where CategorySubscription has a many to one unidirectional relationship with Category. I want to grab a list of Categories c and left join CategorySubscription cs WITH cs.category_id = c.id AND cs.user_id = value.
Can I do this somehow?

Starting with Doctrine version 2.3 you can, as mentioned in a blog.
It's also mentioned in the docs if you know where to look. Scroll down to the last example under "15.2.4. DQL SELECT Examples":
Joins between entities without associations were not possible until version 2.4, where you can generate an arbitrary join with the following syntax:
<?php
$query = $em->createQuery('SELECT u FROM User u JOIN Blacklist b WITH u.email = b.email');
I know it says "not possible until version 2.4", but it definitely works starting with 2.3!

You can try using multiple from() methods and join conditions put in where() or andWhere methods()

Related

Django ORM join many to many relation in one query

If we have 2 models A, B with a many to many relation.
I want to obtain a sql query similar to this:
SELECT *
FROM a LEFT JOIN ab_relation
ON ab_relation.a_id = a.id
JOIN b ON ab_relation.b_id = b.id;
So in django when I try:
A.objects.prefetch_related('bees')
I get 2 queries similar to:
SELECT * FROM a;
SELECT ab_relation.a_id AS prefetch_related_val_a_id, b.*
FROM b JOIN ab_relation ON b.id = ab_relation.b_id
WHERE ab_relation.a_id IN (123, 456... list of all a.id);
Given that A and B have moderately big tables, I find the way django does it too slow for my needs.
The question is: Is it possible to obtain the left join manually written query through the ORM?
Edits to answer some clarifications:
Yes a LEFT OUTER JOIN would be preferable to get all A's in the queryset, not only those with a relation with B (updated sql).
Moderately big means ~4k rows each, and too slow means ~3 seconds (on first load, before redis cache.) Keep in mind there are other queries on the page.
Actually yes, we need only B.one_field but having tried with Prefetch('bees', queryset=B.objects.values('one_field')) an error said you can't use values in a prefetch.
The queryset will be used as options for a multi-select form-field, where we will need to represent A objects that have a relation with B with an extra string from the B.field.
For the direct answer skip to point 6)
Let'ts talk step by step.
1) N:M select. You say you want a query like this:
SELECT *
FROM a JOIN ab_relation ON ab_relation.a_id = a.id
JOIN b ON ab_relation.b_id = b.id;
But this is not a real N:M query, because you are getting only A-B related objects The query should use outer joins. At least like:
SELECT *
FROM a left outer JOIN
ab_relation ON ab_relation.a_id = a.id left outer JOIN
b ON ab_relation.b_id = b.id;
In other cases you are getting only A models with a related B.
2) Read big tables You say "moderately big tables". Then, are you sure you want to read the whole table from database? This is not usual on a web environment to read a lot of data, and, in this case, you can paginate data. May be is not a web app? Why you need to read this big tables? We need context to answer your question. Are you sure you need all fields from both tables?
3) Select * from Are you sure you need all fields from both tables? May be if you read only some values this query will run faster.
A.objects.values( "some_a_field", "anoter_a_field", "Bs__some_b_field" )
4) As summary. ORM is a powerful tool, two single read operations are "fast". I write some ideas but perhaps we need more context to answer your question. What means moderate big tables, wheat means slow, what are you doing with this data, how many fields or bytes has each row from each table, ... .
Editedd Because OP has edited the question.
5) Use right UI controls. You say:
The queryset will be used as options for a multi-select form-field, where we will need to represent A objects that have a relation with B with an extra string from the B.field.
It looks like an anti-pattern to send to client 4k rows for a form. I suggest to you to move to a live control that loads only needed data. For example, filtering by some text. Take a look to django-select2 awesome project.
6) You say
The question is: Is it possible to obtain the left join manually written query through the ORM?
The answer is: Yes, you can do it using values, as I said it on point 3. Sample: Material and ResultatAprenentatge is a N:M relation:
>>> print( Material
.objects
.values( "titol", "resultats_aprenentatge__codi" )
.query )
The query:
SELECT "material_material"."titol",
"ufs_resultataprenentatge"."codi"
FROM "material_material"
LEFT OUTER JOIN "material_material_resultats_aprenentatge"
ON ( "material_material"."id" =
"material_material_resultats_aprenentatge"."material_id" )
LEFT OUTER JOIN "ufs_resultataprenentatge"
ON (
"material_material_resultats_aprenentatge"."resultataprenentatge_id" =
"ufs_resultataprenentatge"."id" )
ORDER BY "material_material"."data_edicio" DESC

How to reference elements from a joined table in a DQL statement

I have two entities (AdminMembers\Entity\Members and AdminEvents\Entity\Invitees) that are joined with a OneToMany relationship. Because the relationship is defined in the entities, I can use the following DQL statement to query the data:
$dql = "SELECT i FROM AdminEvents\Entity\Invitees i WHERE i.eventID=$eventID";
And, through this configuration I can use statements like $invitee->getMember()->getMemberLastName() in my ZF2 view script to get the data from the joined entities.
Now, if I want to sort the data in the DQL statement by fields in AdminMembers\Entity\Members, I run into a problem. The following statement
$dql = "SELECT i FROM AdminEvents\Entity\Invitees i WHERE i.eventID=$eventID ORDER BY i.memberLastName, i.memberFirstName";
throws the error message
Class AdminEvents\Entity\Invitees has no field or association named memberLastName
This is because the fields memberLastName and memberFirstName are defined in AdminMembers\Entity\Members. Although an association does exist, I'm certain I’m just missing some syntax in referencing memberLastName and memberFirstName.
I know that I can expand the DQL statement to join the entities in the DQL statement, which would allow me to identify the joined table and relate the elements to table identifier. But, since the tables are already joined in the entity definitions, they shouldn’t have to be joined again.
Is there a special syntax for referencing joined-table entities in the DQL statement without "rejoining" the tables in the statement?
You should join the members entity to be able to sort by its fields. See docs:
$dql = "SELECT i, m FROM AdminEvents\Entity\Invitees i JOIN i.member m WHERE i.eventID=$eventID ORDER BY m. memberLastName, m.memberFirstName";

Select only specific relation of relatiojn Doctrine 1.2

I have relation of relation of relation query thus and i want to select only the most recently created record for table CancellationRequest c.
Anyone any idea if this is/should be possible and how?
Doctrine_Query::create()
->from('UserNotificationTo unt')
->leftJoin('unt.Notification un')
->leftJoin('un.QuoteOrder qo')
->leftJoin('qo.CancellationRequest c')
->where('un.sent_external = 0')
->andWhere('c.updated_at *IS THE MOST RECENTLY CREATED ONE*')
->execute();
You have to do ORDER BY c.updated_at
Then you can do:
$userNotifcation->getQuoteOrder()->getCancellationRequest()->first()
to get the most recent one

JPA JPQL: select items when attribute of item (list/set) contains another item

public class Document extends Model {
...
#ManyToMany
public Set<User> accessors;
...
}
I want to select all Documents which accessors contain a certain user.
I have just minimal experiences with SQL and no experiences with JPQL.
So how to do that?
thanks in advance
SELECT d FROM Document AS d WHERE :user MEMBER OF d.accessors
Should be what you need, and it is simpler than joining tables.
Just dont forget to use the user as a parameter instead of using its id:
query.setParameter("user", user);
select distinct d from Document d inner join d.accessors a where a.id = :id
You should learn how SQL joins work, and then learn how to use joins in JPQL. That's essential. You'll find plenty of tutorials online. Google is your friend.

Doctrine join filter

I know conditional filters aren't yet available for queries (as per "26.1.4. Applying Filter Rules to any Query" in the Known Limitations section of the Doctrine2 manual) , so I wanted to ask the experts what their preferred solution to the following problem is:
My site has product objects that each have many reviews. The reviews have a status field. I don't want to unnecessarily pull in reviews that haven't been approved during the automatic association that Doctrine2 does so wonderfully.
My current solution/hack is to use single table inheritance (STI) with a discriminator for "status" and have an ApprovedProductReview extending the ProductReview class based on a status of "APPROVED"
I should add that I am currently simply calling
$em->find('Entities\Product', $pid);
to get my product, and Doctrine2 does all the associations automatically. Should I instead be instantiating a product by providing a DQL query?
What I'd really like is a way to override the magic that Doctrine2 provides based on the annotations, and simply be able to use DQL to lazily get the correct subset of reviews.
Suggestions?
You can use the WITH statement in DQL:
$dql = "SELECT p, r
FROM Entities\Product p
LEFT JOIN p.reviews r WITH r.status = :status
WHERE p.id = :id"
$q = $em->createQuery($dql);
$q->setParameter('id', $id);
$q->setParameter('status', $status);
$product = $q->getSingleResult();