Orm 4.0 comes with performance changes for relationships. Let’s see what has been enhanced.
You could already filter a relationship to minimize the number of fetched entities (by filtering or limiting). Let’s say we want to get the 10 latest books that publisher has published.
$books = $publisher->books->toCollection()
->orderBy('publishedAt', ICollection::DESC)
->limitBy(10);
foreach ($books as $book) { /* ... */ }
The code correctly fetches only the needed books and iterates over them.
But, what about the situation when developer creates another book associated with the publisher? Will this code work?
$newBook = new Book();
$newBook->publishedAt = new \DateTimeImmutable();
// + other initialization
$publisher->books->add($newBook);
$books = $publisher->books->toCollection()
->orderBy('publishedAt', ICollection::DESC)
->limitBy(10);
foreach ($books as $book) { /* ... */ }
Yes! It will, however until Orm 4.0 it will behave in quite non-optimized way.
In Orm <4.0 the code will
- fetch all persisted books,
- add the new book to the collection,
- process the collection in-memory (e.g. it may process hundreds of entities),
- return the correct result.
This is changing in Orm 4.0, which will do this the smart(er) way. In Orm 4.0+ the code will:
- fetch all persisted books which match the conditions
(e.g. only the 10 latest books), - add the new book to the collection,
- process the collection in-memory (e.g. it will process only the 11 latest books and take the first 10),
- return the correct result.
From the description you may see that we will do the processing twice (in storage and in-memory), but the number of processed entities should be significantly lower and the memory benefits should be huge.
The earlier behavior could be quite problematic since some your code wouldn’t expected that they work with unpersisted relationships and therefore working with the collection inefficiently.
Thanks @DavidMatejka for coming up with the idea and proof-of-concept implementation.