(Symfony) Doctrine ORM findAll: too much information - php

I was wondering if it is possible to modify $this->getDoctrine()->getRepository('AppBundle:Foo')->findAll(), so that I only get the IDs of the related entities. Because Foo is related to 'one User' and 'multiple Groups', I always get the 'whole User object' and 'ALL Group objects' in the result, which makes the result very unclear. So, is it possible to only print the IDs of the related objects?
I hope someone can help me. Thanks!

You don’t have to retrieve the full entities, you can as well select only the fields you need. Instead of an entity list, you will get a list of plain arrays, where each array contains the selected fields.
$ids = $em->createQueryBuilder() // $em is your entity manager
->select("foo.id")
->from("AppBundle:Foo", "foo")
->getQuery()->getResult();
$ids = array_map("current", $ids);
Note: The last line is optional, it will “flatten” your array when you select only one field.

You will have to write your own custom query:
$query = $this->getDoctrine()->getManager()->createNativeQuery('SELECT id FROM foo');
$foos= $query->getResult();
the above should work
see here for more info http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/native-sql.html

Related

Why is my result array with a hydrated associated entity shaped differently using query builder vs createQuery?

Doctrine's documentation describes two ways to select a record and its relation, one with using a "fetch join" and one not:
Fetch join
Fetch join of the address:
<?php
$query = $em->createQuery("SELECT u, a FROM User u JOIN u.address a WHERE a.city = 'Berlin'");
$users = $query->getResult();
When Doctrine hydrates a query with fetch-join it returns the class in the FROM clause on the root level of the result array. In the previous example an array of User instances is returned and the address of each user is fetched and hydrated into the User#address variable. If you access the address Doctrine does not need to lazy load the association with another query.
The other way
Retrieve a ForumUser and its single associated entity:
<?php
$query = $em->createQuery('SELECT u, a FROM ForumUser u JOIN u.avatar a');
$users = $query->getResult(); // array of ForumUser objects with the avatar association loaded
echo get_class($users[0]->getAvatar());
My question
When I use the query builder it performs like a fetch join and I receive an array with all users and the associations in its root. Like:
$qb = $this->em->createQueryBuilder()
->select('u')
->from(User::class, 'u');
$qb->join(Address::class, 'a', Join::WITH, 'u.a = a');
$qb->addSelect('a');
// Returns
$result = [
'user' => // User
'address' => // Address
];
The User object does also have the Address hydrated, as expected.
However I don't want this, I want it shaped like the second example: an array of just Users, with the Address only accesible via the User object. I could simply filter the array to get the desired shape, but I feel like that's a workaround due to my lack of understanding.
Is it possible to use the query builder to get a result array shaped like the second example above? Or do I have to filter the result array for that? The confusing part for me is that the actual SQL generated by both is identical!
I come from Laravel and Doctrine is brand new to me, so please forgive my naivety on the subject. If I need to clarify anything, just let me know.
Thanks!
I think I found my answer in the related questions section: Symfony2 query builder joins to get objects as a single array
Instead of joining with the fully qualified names of the entities you should join with the association. So the query becomes:
SELECT hs,s,h
FROM \App\SoBundle\Entity\HandsetSubscription hs
JOIN hs.subscription s with s.id = hs.subscription
AND s.mins = 150
AND s.mb = 250
AND s.sms = 150
JOIN hs.handset h with h.id = hs.handset

Doctrine QueryBuilder groupBy relation not working

I have the following query:
$query = $qb->select('p')
->from(get_class($page), 'p')
->innerJoin('p1.translations', 't')
->groupBy('p.id')
->addGroupBy('t.id')
->getQuery();
Doctrine returns the above like:
Page entity -> [translation 1, translation 2, translation 3]
But I want the result like:
Page entity 1 -> translation 1
Page entity 1 -> translation 2
Page entity 1 -> translation 3
Does anyone know how I can do this? I want to return a list of entities.
First of all, both groupBy are completely superfluous, assuming both id fields you are grouping by are the primary keys of their respective tables. For any given (p.id, t.id) combination there will only be at most one result, whether you are grouping or not.
Second, even though you are joining the the translations table, you are not fetching any data from it (t is absent from your ->select()). It just appears that way since doctrine "magically" loads the data in the background when you call $page->getTranslations() (assuming your getter is called that way).
Third, your issue isn't with the groupBy. You are completely misunderstanding what an ORM actually does when hydrating a query result. The SQL query that doctrine generates from your code will actually return results in a fashion just like you expect, with "Page entity 1" repeated multiple times.
However, now comes the hydration step. Doctrine reads the first result row, builds a "Page entity 1" and a "Translation 1" entity, links them and adds them to it's internal entity registry. Then, for the second result, Doctrine notices that it has already hydrated the "Page entity 1" object and (this is the crucial part!) reuses the same object from the first row, adding the second translation to the ->translation collection of the existing object. Even if you read the "Page entity 1" again in a completely different query later in your code, you still get the same PHP object again.
This behaviour is at the core of what Doctrine does. Basically, any table row in the database will always be mirrored by a single object on the PHP side, no matter how often or in what way you actually read it from the database.
To summarize, your query should look like this:
$query = $qb->select('p, t')
->from(get_class($page), 'p')
->innerJoin('p.translations', 't')
->getQuery();
If you really need to iterate over all (p, t) combinations, do so with nested loops:
foreach ($query->getResult() as $p) {
foreach ($p->getTranslations() as $t) {
// do something
}
}
EDIT: If your relationship is bidirectional, you could also turn your query around:
$query = $qb->select('t, p')
->from('My\Bundle\Entity\Translation', 't')
->innerJoin('t.page', 'p')
->getQuery();
Now in your example you actually get 3 results that you can iterate over in a single foreach(). You would still only get a single "Page entity 1" object, with all the translations in the query result pointing to it.

Doctrine 2 delete with query builder

I have two Entities with relation OneToMany, Project and Services. Now i want to remove all the services by project_id.
First attempt:
$qb = $em->createQueryBuilder();
$qb->delete('Services','s');
$qb->andWhere($qb->expr()->eq('s.project_id', ':id'));
$qb->setParameter(':id',$project->getId());
This attempt fails with the Exception Entity Service does not have property project_id. And it's true, that property does not exists, it's only in database table as foreign key.
Second attempt:
$qb = $em->createQueryBuilder();
$qb->delete('Services','s')->innerJoin('s.project','p');
$qb->andWhere($qb->expr()->eq('p.id', ':id'));
$qb->setParameter(':id',$project->getId());
This one generetate a non valid DQL query too.
Any ideas and examples will be welcome.
You're working with DQL, not SQL, so don't reference the IDs in your condition, reference the object instead.
So your first example would be altered to:
$qb = $em->createQueryBuilder();
$qb->delete('Services', 's');
$qb->where('s.project = :project');
$qb->setParameter('project', $project);
If you really can't get project object and you have to handle only with id you can use this.
Citation from doctrine documentation:
There are two possibilities for bulk deletes with Doctrine. You can either issue a single DQL DELETE query or you can iterate over results removing them one at a time. (Below I paste only first solution)
DQL Query
The by far most efficient way for bulk deletes is to use a DQL DELETE query.
Example that worked in my project
$q = $em->createQuery('delete from AcmeMyTestBundle:TemplateBlock tb where tb.template = '.intval($templateId));
$numDeleted = $q->execute();
In entity TemplateBlock I have property called template which is mapped to template_id in db.
But I agree that highly preferred way of doing it is using objects.

Doctrine $record->get() with a JOIN

I'm using the following piece of code to retrieve the tags of a shop:
$tags = $this->getObject()->get('Tag');
$this->getObject() returns a Shop object, and ->get('Tag') returns an array of Tag objects related to this shop.
Here's how my database is arranged: 1 Shop = 1 or more Tag, and 1 Tag = 1 Tag_Translation.
What i'd like to do is to retrieve, instead of an array of Tag objects, and array of Tag objects with their translations (in other words, a kind of JOIN).
How is that possible, keeping that same syntax? Thank you very much, i'm new to Doctrine and ORMs in general, i would have had no problem doing it with MySQL but here ...
You may solve this issue like this
a) You can call Tag Models function, when you need the translation
$tag->getTagTranslation()
b) Or you can overwrite your Shop's getTag() function and build your own Query with DQL as #greg0ire suggested, to fetch translation and tag at once
public function getTag(){
return Doctrine_Query::create()
->from("Tag t")
->leftJoin("t.TagTranslation tt")
->addWhere("t.shop_id = ?", $this->getId())
}
(Of course you can name a new function e.g. getTagsWithTranslation())
This assumes, you have built a schema.yml with proper relations !

Doctrine 2 entities with one to many relations

I'm trying to fetch an annotation with an one to many relation but as soon as I use a join I will end up with the following data:
entities\Topic
id = 1 // integer
title = "example" // string
comments // entities\Comment = oneToMany
id = 1 // integer
comment = "first comment" // string
topic // entities\Topic = manyToOne
id = 1 // again..
title = "example"
Why does doctrine fetch the the manyToOne relation inside comments when I join on the topics comments? This is my query:
$this->em->createQueryBuilder()
->from('Entities\Topic', 't')
->select("t, c")
->leftjoin("t.comments", 'c')
->where('t.id = :id')
->setParameter('id', 1)
->getQuery()->getSingleResult();
Shouldn't the topic property be null or at least an empty arrayCollection?
Another thing:
Why do I get a PersistentCollection back as comments when I specify that comments is an arrayCollection? Do I always need to use unwrap on the PersistentCollection before I can loop through it?
On the first bit - it's likely populating the topic because it already has the data. Were the data not already on-hand, you'd likely have a proxy entity there. It would never be null, because null would be wrong (the comment does have a topic).
As for ArrayCollection/PersistentCollection, you can safely ignore the distinction. I don't know the implementation details, but basically, the EM gives back stuff in PersistentCollections, which I assume play a role in managing the entities in the collection. If you're creating the collection, you use ArrayCollection. Sorry I can't be more specific here, but the bottom line is, you should probably just think about any PersistentCollections you get from the EM as just "a collection"

Categories