The benefit is you’re able to only query for what you need and not be required to load all relationships of a given entity. E.g. if you query for all articles but only need the author’s ID it would be a waste to have extra queries eagerly fetching data you’re not going to use. The ORM I’m used to working with uses the ghost/proxy objects as a way to handle that in a pretty performant way. As the default fetch type is LAZY.
Article ID 1 - Author: Fred (1)
Article ID 2 - Author: Fred (1)
Article ID 3 - Author: Jim (2)
Article ID 4 - Author: Fred (1)
Like this example would result in 3 queries:
- Fetch all the articles
- Fetch author 1
- Fetch author 2
So not as bad as actual N+1, but still valid point. If you know you’ll be using all this data, you can set the fetch mode of the Article.author relationship to EAGER in which case it can then output the same data with just a single query. I found it nice to work with as you have the flexibility to not have to worry about querying selecting too much, but also have the option to improve performance by telling it to fetch eagerly; either all he time or on a specific query.
Another use case for the ghost is you can have partial objects. Like say your Article entity has the contents of the article as a TEXT field. You may not want to load that in all the time to save memory. So could leverage a ghost that represents the article, just excluding that one property/having it be fetched only when accessed.
However, after sleeping on it I realized that maybe the focus of this RFC shouldn’t really be on lazy objects themselves, but instead related to this idea of having objects that are interchangeable even if they don’t implement an explicit interface.
Ultimately this relates to the “implicit interface” idea back in [RFC] Standardized Abstractions - #3 by jhass. Possibly could have special handling for delegate/forward_missing_to (or maybe some new construct) to let the compiler know an object is compatible?
Another open question is how to handle the partial object side of thing. That could probably just be handled in user land via getter! macro for example easily enough tho.