Move result projection onto Table (Table::projectAs/list); drop SelectQuery::reshape()#19484
Move result projection onto Table (Table::projectAs/list); drop SelectQuery::reshape()#19484dereuromark wants to merge 2 commits into
Conversation
7e254e3 to
8bd8f36
Compare
8bd8f36 to
b7ee889
Compare
find()->first() is statically typed as the entity even when a formatter reshapes each row, so reading the result of find(...)->first() gets a type that lies at runtime and hides the reshape from the call site. reshape() makes the reshape explicit and rebinds the result generic, so first()/firstOrFail()/all() resolve to the new shape instead of the entity. Runtime behavior is identical to formatResults(..., OVERWRITE); the only addition is the honest static type via a TNew template bound to EntityInterface or array. Named reshape() rather than map() to avoid confusion with the collection map(), which applies per element and returns a collection; this operates on the whole result set and returns the query.
b7ee889 to
31cbdb7
Compare
There was a problem hiding this comment.
Using the overwrite mode means this method can only be used once? Or rather if used multiple times only the last call will work.
This means it can't be used inside custom finders as if you chain finders like $table->find('foo')->find('bar') and both the finders use reshape(), the callback set by findFoo() will get overwritten.
If finder stacking can't be used then I question the need for this method. One can just do $table->find('foo')->find('bar')->all()->somecollectionMethd() instead.
|
I am fine with this as it is. Looking at our docs we only mention But I know this is primarily focused on getting the static typing move more forward, so we just have to make sure we document this new method properly so users understand why it exsist, when to use it, and when to user alternatives. |
|
Based on the open topics above I think we need to first talk more about the topic itself in the parent issue ( #19482 ). |
Pivots the find-honesty work from augmenting the query class to terminal repository methods, per review feedback: SelectQuery stays a query builder that yields entities or arrays, and shape-changing transforms live on the Table (repository) where the honest return type can be a concrete array. This also sidesteps the overwrite-formatter stacking limit of reshape(), since the terminal methods consume a query and return an array rather than mutating a chainable formatter. - Add Table::projectAs(class-string, ?SelectQuery): list of DTOs. Delegates to the existing query projection path, so rows are mapped from the raw selected columns (entity visibility and accessors do not affect the data). - Add Table::list(?SelectQuery, ...): terminal counterpart to the list finder, returning the combined array directly. - Remove SelectQuery::reshape() (never released). - Soft-deprecate SelectQuery::projectAs() in favor of Table::projectAs(); doc-only for now since it is still used by createFromArray-style DTOs.

Draft. Part of the find()-honesty discussion in #19482.
Problem
$x = $table->find('auth')->first()is statically typed as the entity even when a finder reshapes each row viaformatResults(..., OVERWRITE). The static type lies at runtime, and the reshape is invisible at the assignment site.Direction (revised)
Rather than adding result-reshaping methods to
SelectQuery, this keepsSelectQuerya query builder (it returns entities or arrays) and puts shape-changing transforms on theTable(repository), where the honest return type can be a concrete array. This follows the same trajectory asdisableHydration()->Table::unhydratedFind()/UnhydratedSelectQuery.It also avoids the overwrite-formatter stacking limitation: the terminal repository methods consume a query and return an array instead of mutating a chainable formatter.
What this adds
Table::projectAs(class-string $class, ?SelectQuery $query = null): arrayreturns a concretelist<T>of DTOs. Delegates to the existing query projection path, so rows are mapped from the raw selected columns (entity visibility/accessors do not affect the DTO data).Table::list(?SelectQuery $query = null, ...): arrayis the terminal counterpart to thelistfinder and returns the combined array directly.SelectQuery::reshape()(never released).SelectQuery::projectAs()(doc-only) in favor ofTable::projectAs().Out of scope (need design discussion first, see #19482)
createFromArray()-style DTOs are type-tracked too.SelectQuery::formatResults()/projectAs()in 6.0, which needs an internal result-decorator pipeline first (find('list')/find('threaded')/associations still useformatResults()internally).