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
Related
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?
I have a database, where I store some fixed values like product categories. When I create a new product and I want to assign a category to it, I do it this way:
$categories = new ProductCategoryRepository();
$category = $categories->find(ProductCategory::EXAMPLE);
$product = new Product();
$product->setCategory($category);
However, I'm not sure why I have to lookup the database all the time to get static entities my app is already aware of.
It should be enough to assign the category statically. Maybe something like this:
$category = ProductCategory::EXAMPLE;
Now Doctrine should persist the relation with the correct ID (described by the ProductCategory class (which could be an entity?)) and I no longer have to lookup the database for static properties.
I don't know how to do this, yet. I could create new entities all the time, but this doesn't seem to be correct, because the values are already stored in the DB and they are always the same and not new entities.
$category = new ProductCategory::EXAMPLE;
Fetching the relation from the product however should return the property as an entity:
$category = $product->getCategory();
return $category instanceof ProductCategory; // true
Is there a way to achieve this behaviour?
It is more an architecture question than a performance tweak. I don't want to describe information multiple times (db entries, php constants, entity relations etc.).
There is something called "second level cache" in Doctrine, but the feature is considered experimental and you should maybe read the documentation carefully before using it.
A quote from the official documentation of this feature:
The Second Level Cache
The second level cache functionality is marked as experimental for now. It is a very complex feature and we cannot guarantee yet that it works stable in all cases.
Entity cache definition is done like this: (documentation)
/**
* #Entity
* #Cache(usage="READ_ONLY", region="my_entity_region")
*/
To improve performance for such entities like you are talking about in your question you should also consider to mark them as "read only", which will lead to performance increase from Doctrine 2.1, as can be found in the Doctrine documentation on improving performance:
Read-Only Entities
Starting with Doctrine 2.1 you can mark entities as read only (See metadata mapping references for details). This means that the entity marked as read only is never considered for updates, which means when you call flush on the EntityManager these entities are skipped even if properties changed. Read-Only allows to persist new entities of a kind and remove existing ones, they are just not considered for updates.
The entity should be configured like this: (documentation)
/** #Entity(readOnly=true) */
Second level cache and read only for your ProductCategory:
So after setting up second level read only caching with for example a region named read_only_entity_region your configuration for your ProductCategory would look something like this:
/**
* #Entity(readOnly=true)
* #Cache(usage="READ_ONLY", region="read_only_entity_region")
*/
class ProductCategory
{
//...your entity definition...
}
If you don't want it to hit the database every time you could just store it in the Cache:
public function getCategory(){
return Cache::rememberForever('category-'.$this->category_id, function() {
return $categories->find($this->category_id);
});
}
This will pull the info from the database if it has never been pulled, but will just grab it from the cache if it has been. You would have to use Cache::forget('category-2') to remove it, or php artisan cache:clear. Your static values would just be integer IDs and your products would have a category_id but the categories themselves would be cached.
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
So I've been trying to make a bit of DDD at the project I work on, but I'm facing the problem I mention in the title.
We have the Entity.php generated by the Symfony console, with the Doctrine annotations in there (I know it is not how it should be made), and the corresponding EntityRepository.php.
The applicable object graph is:
Post entity contains a Messages collection, which in turn have a ReadMessagescollection because we need to know by whom has it been read. To know whether a Post has been read, we want to left join Messages with ReadMessages filtering by the user we need, and if there are any blank ReadMessages, we'll know it has not been read.
If we use a method in the Post entity to iterate over all Messages and all ReadMessages for each of those, Doctrine will be making lots of queries unless we configure the associations as Eager, which we don't want to because then it will be retrieving the associations all the times we ask for a Post; the ideal way would be to use a DQL query that loads the joined entities, but since there is no way to access the repository from the entity (apart from injecting one in the other -which I don't even know if is possible-), I think the only option left is to use a Symfony2 service that gets Doctrine injected. The thing is that I don't really like having to add another piece just as a helper.
Is there any other way to do this?
Thanks in advance.
What if you would filter your collection using criteria (Doctrine\Common\Collections\Criteria)? I think this might solve your problem. You can read on how to do this in detail in the Doctrine2 documentation in 8.8.
It is as simple as you define your Criteria as where message is read and then you get the filtered result as follows:
/**
* Get all read messages
*
* #return Collection
*/
public function getReadMessages(){
$isReadCriteria = //... define criteria
$messages = $this->getMessages();
$readMessages = $messages->matching($isReadCriteria);
return $readMessages;
}
Multiple entities in my application have a gallery associated, defined this way
/**
* #ORM\ManyToOne(targetEntity="Gallery")
* #ORM\JoinColumn(name="gallery_id", referencedColumnName="id")
*/
protected $gallery;
I like to perform some custom logic when those entities are loaded, using postLoad doctrine event. I eventually learnt that Doctrine doesn't advice to play with associations on postLoad event, as they are not fully loaded...
In order to improve performance, I added a fetch="EAGER" on this association. Which is nice to reduce the number of requests while fetching a collection.
But now the getGallery() is empty in the postLoad event.
Is there any way to trigger something after the associations have been performed ? I could wrap a service and/or model around my entity to do the logic but I would prefer a simple event.
Thanks !