Repository navigation
Merge release 3.7.6 into 3.8.x - #12721
Merged
Merged
Conversation
Unnamed indexes and unique constraints only get a positional key, so addInheritedIndexes() dropped the first unnamed index of a mapped superclass as soon as the entity declared one of its own. Append unnamed entries of the superclass unless the entity already declares one on the same columns or fields, and keep overriding named entries by name.
paginate() now calls useQueryCache(false) on the cloned sub-query, so each page still runs CursorWalker and the cursor walk does not write a cache entry. The walker has to keep running because the order-by metadata used to build the next cursor is only available when it runs. The new test paginates the same DQL with a null cursor and three Cursor values against an ArrayAdapter, asserts the cache stays empty, then calls getSQL() on the original query twice and asserts that a single entry remains. Fixes #12649
Name the two cases a cached ParserResult would break (a repeated cursor and a repeated first page) instead of "later pages". The second getSQL() call in the test returned the already parsed ParserResult without touching the query cache, so its assertion could never fail.
The LimitSubqueryWalker replaces the select clause with the root identifier, so a parameter only used in the SELECT clause is no longer used by the id query, which then fails with "Too many parameters". Unbind the unused parameters like the other paginators do, keeping the cursor parameters.
The cursor parameters are bound when the query is executed, so the parse detecting unused parameters does not need the cursor hints.
The cursor condition for a multi-column ORDER BY is expanded into an OR chain, (a < :a) OR (a = :a AND b < :b). PostgreSQL cannot derive an index range from that form, so it applies it as a filter and the cost of fetching a page grows with its depth.
Add a leading bound to the cursor pagination predicate
…ery-cache Stop CursorPaginator writing a query-cache entry per cursor
…r-unbind-unused-parameters Unbind unused parameters in CursorPaginator without output walkers
When a custom ResultSetMapping is set on a query, as the Paginator does for its count query, the discriminator parameters recorded by the SqlWalker in the parsed RSM were ignored: a ClassMetadata bound to an INSTANCE OF expression was bound as its FQCN instead of the discriminator values, so count() always returned 0 while iterating the very same Paginator returned the expected results. Resolve discriminator parameters against the parser result RSM, which is always populated once the query has been parsed, keeping the user-provided RSM as a fallback. Fixes #7039 Co-authored-by: Si-gi <hidji@protonmail.com>
ProxyFactory::getProxy() assigns an embedded identifier through EmbeddablePropertyAccessor::setValue(), which first reads the embeddable. TypedNoDefaultPropertyAccessor::getValue() called isInitialized() on the lazy property, which initializes the whole object, so the proxy ended up registered as initialized with an empty snapshot and fully rewritten on the next flush. Fixes #12674
The contract was implicit. Reading or writing a mapped property must not trigger the initialization of a lazy object, because the UnitOfWork uses these accessors to compute changesets and identifiers. State it on the interface, so that an implementation relying on isInitialized() or getValue() on a lazy object stands out as a violation.
…rties Unsetting a property that is still lazy initializes the whole object. The property is already unset in that case, so the call can be skipped.
Cover getValue() and setValue(null) on a lazy ghost, and assert that the identifier of a to-one proxy with an embedded identifier stays readable without initializing the proxy.
…itialize them The hydrator reads the current value of a collection property through the property accessor before merging the fetched elements. On a partial lazy object, that read went through isInitialized(), which initialized the object and ran one extra query per row: 4 queries on PHP 8.4, 2 on PHP 8.5. Test written by George Tudoran, who reported the case on #12675. Co-Authored-By: George Tudoran <george.tudoran@zitec.com>
Use DBAL's configured folding for unquoted identifiers while preserving legacy casing on DBAL platforms without the folding API. Fixes #12272.
…typed properties (#12675) * Do not initialize a lazy object when reading one of its typed properties ProxyFactory::getProxy() assigns an embedded identifier through EmbeddablePropertyAccessor::setValue(), which first reads the embeddable. TypedNoDefaultPropertyAccessor::getValue() called isInitialized() on the lazy property, which initializes the whole object, so the proxy ended up registered as initialized with an empty snapshot and fully rewritten on the next flush. Fixes #12674 * Document that property accessors must not initialize lazy objects The contract was implicit. Reading or writing a mapped property must not trigger the initialization of a lazy object, because the UnitOfWork uses these accessors to compute changesets and identifiers. State it on the interface, so that an implementation relying on isInitialized() or getValue() on a lazy object stands out as a violation. * Do not initialize a lazy object when unsetting one of its typed properties Unsetting a property that is still lazy initializes the whole object. The property is already unset in that case, so the call can be skipped. * Test that property accessors do not initialize lazy objects Cover getValue() and setValue(null) on a lazy ghost, and assert that the identifier of a to-one proxy with an embedded identifier stays readable without initializing the proxy. * Test that fetch-joining a collection into partial objects does not initialize them The hydrator reads the current value of a collection property through the property accessor before merging the fetched elements. On a partial lazy object, that read went through isInitialized(), which initialized the object and ran one extra query per row: 4 queries on PHP 8.4, 2 on PHP 8.5. Test written by George Tudoran, who reported the case on #12675. Co-Authored-By: George Tudoran <george.tudoran@zitec.com> --------- Co-authored-by: Jérôme Tamarelle <jerome@tamarelle.net> Co-authored-by: George Tudoran <george.tudoran@zitec.com>
…ed-indexes-3.7.x Inherit unnamed indexes from mapped superclasses
…fier-folding Respect configured identifier folding
…3.8.x_Kpnl50BO # Conflicts: # tests/Tests/ORM/Functional/PaginationTest.php
greg0ire
approved these changes
Oct 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release Notes for 3.7.6
3.7.x bugfix release (patch)
3.7.6
Bug
Improvement