Say I had the following query that returns a User entity with the associated Post entities that have been flagged (this is just for demonstration):
SELECT u, p
FROM User u
LEFT JOIN u.posts p
WHERE p.isFlagged = true
Once I've used this, I then want to access all the posts for that user, regardless of isFlagged. Is there a simple way to refresh the $user->posts collection so that it is then a complete collection of all the users posts?
I don't want to just pull out all of the posts in the query (as they may not be needed), and the code that needs the complete collection isn't going to be aware of the query this entity came from.
What you are doing here is broken:
SELECT
u, p
FROM
User u
JOIN
u.posts p
WITH
p.isFlagged = true
This will hydrate `User#posts" collection with wrong values, which results in broken logic, broken object graph, broken everything.
You should solve this at DQL level with something like following:
SELECT
u, p
FROM
User u
JOIN
u.posts p
JOIN
u.posts j
WITH
j.isFlagged = true
This will basically hydrate the correct posts collection in the user object, without any intermediate (broken) state.
EDIT: I misunderstood the question because I've based my thoughts on #Athlan's answer, which is fundamentally wrong (flushing a broken collection is really wrong). This is my previous answer, which I still believe is interesting because it solves the problem, but not in a really correct way.
This question actually made me curious to see if the ORM refreshed collections as I expected it to do.
I wrote the test in this branch.
Basically, what you need to do is simply:
$entityManager->refresh($entity);
Here's the relevant part of the test:
$foo = new DDC2666Foo();
$this->_em->persist($foo);
$this->_em->flush();
$this->_em->clear();
$fetchedFoo = $this->_em->find(__NAMESPACE__ . '\DDC2666Foo', $foo->id);
$fetchedFoo->bars->add(new DDC2666Bar());
$this->assertCount(1, $fetchedFoo->bars);
$this->_em->refresh($fetchedFoo);
$this->assertCount(0, $fetchedFoo->bars, 'The collection was reset');
There's just the disadvantage that this will also re-fetch your entity, but the ORM itself does not provide a facade to refresh single collections.
That is also a good thing anyway, since this way, you won't break encapsulation, which may lead to unexpected (and hard to debug) behaviour in your code.
I had this same issue and ended up here. Refreshing isn't an option for me because I have a list and I don't want to make those extra queries.
What you really need here is to add a "fake" join to do the filtering, and leave the original one without a condition... to make this clear:
SELECT u, p
FROM User u
LEFT JOIN u.posts p
LEFT JOIN u.posts p2
WHERE p2.isFlagged = true
Note that I'm leaving p2 out from the select clause, it's just for filtering purposes.
I find the other solutions very non-satisfying because many entities will get hydrated that I'm not at all interested in. If the vast majority of the posts have isFlagged = 0 then the performance impact is huge.
Here's an alternate solution which involves swapping around the relationship between the entities. However it is not entirely equivalent to what the original question asked for. Yet it might be useful to others having this issue:
SELECT p, u
FROM Post p
JOIN p.user u
WHERE p.isFlagged = true
The differences between this and the other solutions is that it will no longer return users that do not have any posts or any flagged posts. This may or may not be what you want.
Related
I am so convenient using built-in codedigniter query builder.
I have to output searching to an existing page since that page involves multi-table.
Currently, I already have a lot of if there,
I am curious how to check if statement db->where() or db->like() have been called or not?
My second option is to construct new query that join 2 tables, but my table does not have any direct relationship, I try to use $this->db->join() it fails on the join condition. I am lack of example of using join, especially the join type. I am expecting to use something like
SELECT * FROM p JOIN hs ON hs.id=%-p.id-%
I am already stuck here, and I am aware that the db->get() will erase the data filtering.
Any suggestion?
I'm currently building an eCommerce site using Symfony 3 that supports multiple languages, and have realised they way I've designed the Product entity will require joining multiple other entities on using DQL/the query builder to load up things like the translations, product reviews and discounts/special offers. but this means I am going to have a block of joins that are going to be the same in multiple repositories which seems wrong as that leads to having to hunt out all these blocks if we ever need to add or change a join to load in extra product data.
For example in my CartRepository's loadCart() function I have a DQL query like this:
SELECT c,i,p,pd,pt,ps FROM
AppBundle:Cart c
join c.items i
join i.product p
left join p.productDiscount pd
join p.productTranslation pt
left join p.productSpecial ps
where c.id = :id
I will end up with something similar in the SectionRepository when I'm showing the list of products on that page, what is the correct way to deal with this? Is there some place I can centrally define the list of entities needed to be loaded for the joined entity (Product in this case) to be complete. I realise I could just use lazy loading, but that would lead to a large amount of queries being run on pages like the section page (a section showing 40 products would need to run 121 queries with the above example instead of 1 if I use a properly joined query).
One approach (this is just off the top of my head, someone may have a better approach). You could reasonably easily have a centralised querybuilder function/service that would do that. The querybuilder is very nice for programattically building queries. The key difference would be the root entity and the filtering entity.
E.g. something like this. Note of course these would not all be in the same place (they might be across a few services, repositories etc), it's just an example of an approach to consider.
public function getCartBaseQuery($cartId, $joinAlias = 'o') {
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select($joinAlias)
->from('AppBundle:Cart', 'c')
->join('c.items', $joinAlias)
->where($qb->expr()->eq('c.id', ':cartId'))
->setParameter('cartId', $cartId);
return $qb;
}
public function addProductQueryToItem($qb, $alias) {
/** #var QueryBuilder $query */
$qb
->addSelect('p, pd, pt, ps')
->join($alias.'product', 'p')
->leftJoin('p.productDiscount', 'pd')
->join('p.productTranslation', 'pt')
->join('p.productSpecial', 'ps')
;
return $qb;
}
public function loadCart($cartId) {
$qbcart = $someServiceOrRepository->getCartBaseQuery($cartId);
$qbcart = $someServiceOrRepository->addProductQueryToItem($qbcart);
return $qbcart->getQuery()->getResult();
}
Like I said, just one possible approach, but hopefully it gives you some ideas and a start at solving the issue.
Note: If you religiously use the same join alias for the entity you attach your product data to you would not even have to specify it in the calls (but I would make it configurable myself).
There is no single correct answer to your question.
But if I have to make a suggestion, I'd say to take a look at CQRS (http://martinfowler.com/bliki/CQRS.html) which basically means you have a separated read model.
To make this as simple as possibile, let's say that you build a separate "extended_product" table where all data are already joined and de-normalized. This table may be populated at regular intervals with a background task, or by a command that gets triggered each time you update a product or related entity.
When you need to read products data, you query this table instead of the original one. Of course, nothing prevents you from having many different extended table with your data arranged in a separate way.
In some way it's a concept very similar to database "views", except that:
it is faster, because you query an actual table
since you create that table via code, you are not limited to a single SQL query to process data (think filters, aggregations, and so on)
I am aware this is not exactly an "answer", but hopefully it may give you some good ideas on how to fix your problem.
I am relatively new to Doctrine and am having an absolute nightmare trying to get a join between two entities returned correctly. I don't really understand the DRM that Doctrine uses as much as I would like at the moment so it really is just a case of trial and error at the moment.
I have two entities, one is document and one is document-sow-detail. I need to join the two where the document_id exists in the document-dow-detail table/entity as to get a couple of fields that exist in the document-dow-detail entity returned.
So, within my document repository, I have attempted the following code (with not a lot of understanding of what is happening). Can someone point me as to what is happening here and why it is not working?
$db = $this->createQueryBuilder($this->alias);
$db->addSelect("dsd");
$db->leftJoin("doc_sow_detail",'dsd','ON',"id_document");
When I breakpoint and evaluate the $db->getQuery() function in my IDE this is the _dql that has been generated.
SELECT d, dsd FROM BillingBundle\Entity\Document d LEFT JOIN doc_sow_detail dsd ON id_document
Can anyone give me a clue as to what I am doing wrong here before I chuck my computer out the window?
Thanks!
A simple example of doctrine join which joins product category on category:
$qb = $this->_em->createQueryBuilder();
$qb->select('p')
->from('BRBProductBundle:Product', 'p')
->join('p.category', 'c')
->where('1 = 1');
Hope this may help you.
I thought I'd post my solution.
I had to change my entity structures due to the one-way relationship between the two entities.
I moved the required entity properties onto the document entity which negated my need for a join. This is probably where these properties needed to be in the first place.
I have a problem with a DQL query and entity specialization.
I have an Entity called Auction, which is OneToOne relation with Item. Item is a mappedSuperclass for Film and Book. I need a query that could back a search engine, allowing the user to look for auctions with different properties AND selling items with different properties (it is the AND part that makes it challenging).
The problem is that even though Auction has an association pointing to Item as such, I need to have access to Film- and Book-specific fields. The users will specify the Item type they're looking for, but I don't see any way of using this information other than using INSTANCE OF in my DQL query.
So far, I have tried using a query like:
SELECT a FROM Entities\Auction a
INNER JOIN a.item i
INNER JOIN i.bookTypes b
WHERE i INSTANCE OF Entities\Book
AND b.type = 'Fantasy'
AND ...".
Such a query results in an error saying that:
Class Entities\Item has no field or association named bookTypes
which is false for Book, yet true for Item.
I have also tried
SELECT a FROM Entities\Book i
INNER JOIN i.auction a ...
but I reckon Doctrine requires that I refer to the same Entity in SELECT and FROM statements.
If that's of importance, I am using class table inheritance. Still, I don't think switching to single table inheritance would do the trick.
Any ideas?
As Matt stated, this is an old issue that Doctrine Project won't fix (DDC-16).
The problem is that doctrine's DQL is a statically typed language that comes with a certain amount of complexity in its internals.
We thought about allowing upcasting a couple of times, but the effort to get that working is simply not worth it, and people would simply abuse the syntax doing very dangerous things.
As stated on DDC-16, it is also indeed not possible to understand which class the property belongs to without incurring in nasty problems such as multiple subclasses defining same properties with different column names.
If you want to filter data in subclasses in a CTI or JTI, you may use the technique that I've described at https://stackoverflow.com/a/14854067/347063 . That couples your DQL with all involved subclasses.
The DQL you would need in your case is most probably (assuming that Entities\Book is a subclass of Entities\Item):
SELECT
a
FROM
Entities\Auction a
INNER JOIN
a.item i
INNER JOIN
i.bookTypes b
WHERE
i.id IN (
SELECT
b.id
FROM
Entities\Book b
WHERE
b.type = 'Fantasy'
)
That is the pseudo-code for your problem. It is not nice, but keep in mind that SQL and DQL are very different and follow different rules.
Updated:
I've discovered a solution for this. See my answer for this related question:
Doctrine2: Polymorphic Queries: Searching on properties of subclasses
You can easily solve this by left-joining your base entity with your inheritance class using the id:
SELECT a FROM Entities\Auction a
INNER JOIN a.item i
INNER JOIN Entities\Book b WITH b.id = i.id
INNER JOIN b.bookTypes bt
WHERE bt.type = 'Fantasy'
AND...
or with a queryBuilder:
$queryBuilderb->select('a')
->from('Entities\Auction', 'a')
->innerJoin('a.item', 'i')
->innerJoin('Entities\Book', 'b', 'WITH', 'b.id = i.id')
->innerJoin('b.bookTypes', 'bt')
->where('bt.type = :type')
->andWhere(...
->setParameter('type', 'Fantasy');
This is based on the answer given by Ian Philips in the question here
The Doctrine team has stated that they're not going to add support for this:
https://github.com/doctrine/orm/issues/2237
Pertinent comments from that page:
Thats indeed tricky. That syntax alone can, however, never work,
because there might be several subclasses that have a field named "d",
so Doctrine would not know which field you mean.
I am closing this one.
The requirement of this issue is basically violating OO principles.
If you really need to filter across multiple child-entities in your
inheritance, then try something as following instead:
SELECT
r FROM
Root r WHERE
r.id IN (
SELECT
c.id
FROM
Child c
WHERE
c.field = :value
)
I had the same issue, and didn't find a solution without using separate queries for each subclass and merging them later on the application level.
One thing I'm sure, single table inheritance will not solve this, completely the same thing.
There is another alternative, although being logically dirty.
Define all the fields (the ones you need) in the superclass. If the record logically doesn't have that field it will be empty. Not a pretty sight, but hey, more optimized than 2-3-4-... queries. Also in this scenario single table inheritance is definitely the better way to go
This situation is pretty difficult to explain, but I'll do my best.
For school, we have to create a web application (written in PHP) which allows teachers to manage their students' projects and allow these to make peer-evaluation. As there are many students, every projects has multiple projectgroups (and ofcourse you should only peer-evaluate your own group members).
My databasestructure looks like this at the moment:
Table users: contains all user info (user_id is primary)
Table: projects: Contains a project_id, a name, a description and a start date.
So far this is pretty easy. But now it gets more difficult.
Table groups: Contains a group_id, a groupname and as a group is specific for a project, it also holds a project_id.
Table groupmembers: A group contains multiple users, but users can be in multiple groups (as they can be active in multiple projects). So this table contains a user_id and a group_id to link these.
At last, admins can decide when users need to do their peer-evaluation and how much time they have for it. So there is a last table evaluations containing an evaluation_id, a start and end date and a project_id (the actual evaluations are stored in a sixth table, which is not relevant for now).
I think this is a good design, but it gets harder when I actually have to use this data. I would like to show a list of evaluations you still have to fill in. The only thing you know is your user_id as this is stored in the session.
So this would have to be done:
1) Run a query on groupmembers to see in which groups the user is.
2) With this result, run a query on groups to see to which projects these groups are related.
3) Now that we know what projects the user is in, the evaluations table should be queried to see if there are ongoing evaluations for this projects.
4) We now know which evaluations are available, but now we also need to check the sixth table to see if the user has already completed this evaluation.
All these steps are dependent on the result of each other, so they should all contain their own error handling. Once the user has chosen the evaluation they wish to fill in (a evaluationID will be send via GET), a lot of new queries will have to be run to check which users this member has in his group and will have to evaluate and another check to see which other groupmembers are already evaluated).
As you see, this is quite complex. With all the errorhandling included, my script will be a real mess. Someone told me a "view" might help in this situation, but I don't really understand why this would help me here.
Is there a good way to do this?
Thank you very much!
you are thinking too procedurally.
all your conditions should be easily entered into one single where clause of a sql statement.
you will end up with a single list of the items to be evaluated. only one list, only one set of error handling.
Not sure if this is exactly right, but try this basic approach. I didn't run this against an actual database so the syntax may need to be tweaked.
select p.project_name
from projects p inner join evaluations e on p.project_id = e.project_id
where p.project_id in (
select project_id
from projects p inner join groups g on p.project_id = g.project_id
inner join groupmembers gm on gm.group_id = g.group_id
where gm.user_id = $_SESSION['user_id'])
Also, you'll need to make sure that you properly escape your user_id when making it a part of the query, but that is a whole other topic.