Doctrine 2.8 forces eager loading - php

I am using Symfony 5.4 with Doctrine 2.8 and I am trying to load an array of entities via query builder, but I don't need any of this entity's relations. Unfortunately, all the relations load no matter what I do, resulting in lots of unnecessary queries. Even when I set the all of its relation annotations to lazy load, it loads them all anyway.
My query is very simple as I only need an array of User entities to fill out a Symfony form with their ids and names:
$this->createQueryBuilder('u')->orderBy('u.last_name', 'ASC')->getQuery()->getResult();
I've also tried it this way as well:
$this->getEntityManager()->createQuery('SELECT u.id, u.first_name, u.last_name from App\Entity\User u')->getResult();
I have tried setting each relation's annotation in the entity class like so:
#ORM\OneToOne(targetEntity=Address::class, mappedBy="user", fetch="LAZY")
or even:
#ORM\OneToOne(targetEntity=Address::class, mappedBy="user", fetch="EXTRA_LAZY")
And I still get numerous queries trying to pull all its relations.
The only solution that works thus far is to comment out all the relations in the entity class, which of course is not a solution at all. Is there a way to force Doctrine to stop eager loading all the User relations?

Related

Symfony and Doctrine - Multiple single table inheritance entities with an optional many to one relationship

I'm having an issue with single table inheritance and an optional many to one mapping on a sub class. When I try to load the parent entity through the child of a subclass the parent is loaded with no data. I can reload the entity and get data without issue, though. Here is a repository with an example:
https://bitbucket.org/GDIBass/relationalsingletableinheritancedoctrinebug
I've tried to recreate this in Doctrine ORM's test suite without any luck. Any ideas?
Try to use eager loading for 'fetch' option in your realtion.
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html

Symfony and Doctrine: lazy loading is not working

Using Symfony 2.8.
I have Community and MenuItem entities, where a Community has a set of MenuItems.
Community.php has the following code:
...
/**
* #ORM\OneToMany(targetEntity="MenuItem", mappedBy="community", fetch="LAZY")
* #ORM\OrderBy({"sequence" = "ASC"})
*/
private $menuItems;
...
MenuItem.php has the following code:
...
/**
* #var Community
*
* #ORM\ManyToOne(targetEntity="Community", inversedBy="menuItems")
*/
private $community;
...
The point is, when I use:
$menuItems = $community->getMenuItems();
the $menuItems variable will be an empty collection.
The problem can be solved by setting fetch="EAGER" instead of fetch="LAZY", because in that way the $menuItems attribute of the Category entity is loaded immediatly.
LAZY vs EAGER (source) :
Whenever you have a managed entity instance at hand, you can traverse and use any associations of that entity that are configured LAZY as if they were in-memory already. Doctrine will automatically load the associated objects on demand through the concept of lazy-loading.
Whenever you query for an entity that has persistent associations and these associations are mapped as EAGER, they will automatically be loaded together with the entity being queried and is thus immediately available to your application.
The point is that while EAGER loading is working as expected, LAZY loading seems not working at all. Any idea about why?
This seems to work to load the lazy relationship.
$logs = $entity->getLogs(); // lazy relationship
$this->getDoctrine()->getManager()->initializeObject($logs);
$logs will now populate.
Docs for initializeObject:
Helper method to initialize a lazy loading proxy or persistent
collection.
When you are doing $community->getMenuItems(); :
In EAGER mode: the data are already fetched, so the array is returned.
In LAZY mode: the database request is done when you do the call. Behind the scene it works by generating "proxies" in front of your entities that will call doctrine for you.
Carefull with lazy loading :
Traversing the object graph for parts that are lazy-loaded will easily trigger lots of SQL queries and will perform badly if used to heavily.
It's better to fetch directly the data, by doing a DQL fetch. See for instance http://blog.bemycto.com/good-practices/2015-05-31/understanding-doctrine-orm-lazy-load-fetch-join/
Personally, I'm not a fan of lazy/eager loading as many queries will be fired when they can be done in one query with joins.
Please see my answer here on how to implement a custom repository to build a custom query, link

How does the Doctrine Repository mechanism of lazy loading entities work?

So, I want to understand how the Doctrine Repository mechanism works.
For my entities I use annotations, so the resulting object is built somewhere during the execution of the script.
I'd like to unserstand which are the possibile ways of implementing the lazy loading of entities from another entity.
In concrete, using Doctrine, I have the ability to fetch information of related object (from the Symfony book). This fetching is done in a lazy way: only if I call the method to get the information about the Entity it is loaded from the database querying it.
Now, I'd like to better understand this mechanism: how an entity can implement repository methods?
How can I reproduce this mechanism to implement it in other context similar to the one of a database data retrieval?
As the resulting object is really big, is there someone who can put me on the right way?
Which classes should have I read to understand the mechanism?
Are there any articles/posts that better explain how this mechanism is implemented?
Are there better (or simply simpler) ways of implementing it?
I think the best description of the lazy loading can be found in Doctrine developer articles.
http://www.giorgiosironi.com/2009/07/lazy-loading-of-objects-from-database.html
http://www.giorgiosironi.com/2009/08/doctrine-2-now-has-lazy-loading.html
The main idea is to insert into Product's category list a set of objectes that are subclasses of Category. These and called "proxy objects" and created "on the fly" when Product is retrieved from database. These proxy objects have the same interface as Category object, but add functionality of loading actual Category items from database when needed.
Doctrine actually creates a extra object (think proxy) that keeps a record of what properties have actually been accessed.
See this part from the documentation :
32.4.2. Association proxies
The second most important situation where Doctrine uses proxy objects is when querying for objects. Whenever you query for an object that has a single-valued association to another object that is configured LAZY, without joining that association in the same query, Doctrine puts proxy objects in place where normally the associated object would be. Just like other proxies it will transparently initialize itself on first access.
doctrine documentation
You can create a custom repository for an entity: http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes
Once you have your own custom repository class you can create queries that fetch all of the information you need in one query rather than relying on lazy loading.
So say you have an Product entity which has one or more Category entities using a ManyToMany relationship you could create a function in your custom repository to fetch all products with their categories in one query:
public function fetchProductsWithCategories()
{
return $this->getEntityManager()
->createQuery(
'SELECT p, c FROM Product p join p.categories c'
)
->getResult();
}
Then in your controller you would have something like:
$repo = $this->getDoctrine()->getManager()->getRepository('Product');
$products = $repo->fetchProductsWithCategories();
Edit: missed c in select

Doctrine many-to-many join without association

Currently I'm working on doctrine module for favorites which can be reusable in any project and for any entity.
However there is problem with JOINs. I followed this article about dynamic mappings and it works great.. Well almost.
I've got User, Article and FavoriteItem entities, where Article entity can be added to favorites. Probably link to that github project with readme would be better (link).
The problem is mainly in that method which should return FavoriteItem by User and IFavoritableEntity, which in our example will be Article. As you can see, that method uses native query, but is it possible to use DQL? I know that I would have to use join to table without association, but it seems that doctrine can do that just for one-to-many/many-to-one associations.. I'm right? Or is there any other way how to do that in DQL?
Also do you think there is any way at all how to select in one query (DQL) all IFavoritableEntities by one User? I just can't imagine any.
Thank you
So I found other option which is add possibility to extend FavoriteItem entity where I'm able to add field with association to eg. Article::favorites field.

Is it possible to have Symfony and/or Doctrine hydrate associated objects managed by different entity managers?

I have a legacy application that was using Xaraya to manage user content that I am trying to replace with a rewrite using Symfony/Sonata to manage users and/or content.
For whatever reason, previous developers managed this with two different databases (MySQL for Xaraya, and SQL Server for other things, including authenticating users).
I am trying to create Entity mappings such that the users/groups from SonataUserBundle (which extends FOSUserBundle) use the entity manager associated with the login database connection, and this works for logging into the admin site itself, but blows up when it tries to hydrate objects that have associations to the User entity.
It appears that Doctrine does not try to find the entity manager associated with an entity when hydrating an object's associations.
My question is this: it it possible to make Doctrine hydrate objects using the entity manager for an entity instead of assuming it's mapped to the current entity manager, and if not, is there any form of a clean code work-around for it?
Thanks.
(Note: The method of using the "databasename.tablename" syntax in the query that I have seen mentioned elsewhere will not work for my use case.)

Categories