From 56b1a166ea70ef0584a45f10cf99f5250709748b Mon Sep 17 00:00:00 2001 From: mscherer Date: Sun, 17 May 2026 21:37:39 +0200 Subject: [PATCH] Document Table::findUnhydrated() and SelectUnhydratedQuery Adds a 'Getting Arrays Instead of Entities' section covering findUnhydrated() as the type-safe replacement for find()->disableHydration(), updates the buffered-results and mapReduce examples to use it, and records the new feature plus the disableHydration() deprecation in the 5.4 migration guide. --- docs/en/appendices/5-4-migration-guide.md | 12 +++++ docs/en/orm/retrieving-data-and-resultsets.md | 52 ++++++++++++++----- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/docs/en/appendices/5-4-migration-guide.md b/docs/en/appendices/5-4-migration-guide.md index aa7986d036..3a876ee869 100644 --- a/docs/en/appendices/5-4-migration-guide.md +++ b/docs/en/appendices/5-4-migration-guide.md @@ -79,6 +79,13 @@ See [Application and Plugin Events](../core-libraries/events#registering-event-l - The `Mailer::$name` property is unused and has been deprecated. +### ORM + +- `SelectQuery::disableHydration()` has been deprecated. Use + [`Table::findUnhydrated()`](../orm/retrieving-data-and-resultsets#getting-arrays-instead-of-entities) + instead, which returns a `SelectUnhydratedQuery` whose static type matches the + array result shape. `disableHydration()` will be removed in 6.0. + ## New Features ### Core @@ -172,6 +179,11 @@ See [Application and Plugin Events](../core-libraries/events#registering-event-l - The `associated` option in `newEntity()` and `patchEntity()` now supports nested array format matching `contain()` syntax. See [Converting Request Data into Entities](../orm/saving-data#converting-request-data-into-entities). +- Added `Table::findUnhydrated()` and the `SelectUnhydratedQuery` class for + type-safe non-hydrated reads. Unlike `find()->disableHydration()`, the + returned query's static type matches its array result shape, so static + analyzers no longer see `entity|array` on `first()`, `all()`, `toArray()` + and iteration. See [Getting Arrays Instead of Entities](../orm/retrieving-data-and-resultsets#getting-arrays-instead-of-entities). ### Testsuite diff --git a/docs/en/orm/retrieving-data-and-resultsets.md b/docs/en/orm/retrieving-data-and-resultsets.md index 132b9fa6d4..3749e2638b 100644 --- a/docs/en/orm/retrieving-data-and-resultsets.md +++ b/docs/en/orm/retrieving-data-and-resultsets.md @@ -179,16 +179,46 @@ you can pass query objects to your controllers, we recommend that you package your queries up as [Custom Find Methods](#custom-find-methods) instead. Using custom finder methods will let you re-use your queries and make testing easier. -By default, queries and result sets will return [Entities](../orm/entities) objects. You -can retrieve basic arrays by disabling hydration: + + +### Getting Arrays Instead of Entities + +By default, queries and result sets return [Entities](../orm/entities) objects. +When you only need plain arrays, use `findUnhydrated()` instead of `find()`: ```php -$query->disableHydration(); +$query = $articles->findUnhydrated(); -// $data is ResultSet that contains array data. +// $data is a ResultSet that contains array data. $data = $query->all(); + +// Terminal methods are typed as arrays too. +$row = $articles->findUnhydrated()->where(['id' => 1])->first(); // array|null +``` + +`findUnhydrated()` accepts the same finder type and arguments as `find()`, so +your existing [custom finders](#custom-find-methods) are reused unchanged: + +```php +$rows = $articles->findUnhydrated('published')->all(); ``` +It returns a `Cake\ORM\Query\SelectUnhydratedQuery`. This behaves exactly like +`find()->disableHydration()` at runtime, but its static type matches the array +result shape, so static analyzers no longer see `entity|array` on `first()`, +`firstOrFail()`, `all()`, `toArray()` and iteration — including after the query +flows through a custom finder. + +> [!NOTE] +> `findUnhydrated()` only changes the result shape for row-returning finders. +> `findList()` and `findThreaded()` produce a key/value map or nested tree +> regardless of hydration, so there is nothing to type differently for them. + +> [!WARNING] +> `SelectQuery::disableHydration()` is deprecated as of 5.4.0 and will be +> removed in 6.0. The fluent toggle returns a query whose static type still +> claims to produce entities; prefer `findUnhydrated()` instead. + ## Getting the First Result @@ -1071,15 +1101,15 @@ section show how you can add calculated fields, or replace the result set. > ->all(); > ``` > -> Depending on your use case, you may also consider using disabling hydration: +> Depending on your use case, you may also consider skipping hydration: > > ``` bash -> $results = $articles->find() -> ->disableHydration() +> $results = $articles->findUnhydrated() > ->all(); > ``` > -> The above will disable creation of entity objects and return rows as arrays instead. +> The above will skip creation of entity objects and return rows as arrays instead. +> See [Getting Arrays Instead of Entities](#getting-arrays-instead-of-entities). ### Getting the First & Last Record From a ResultSet @@ -1244,10 +1274,9 @@ $reducer = function ($occurrences, $word, $mapReduce) { Finally, we put everything together: ```php -$wordCount = $articles->find() +$wordCount = $articles->findUnhydrated() ->where(['published' => true]) ->andWhere(['published_date >=' => new DateTime('2014-01-01')]) - ->disableHydration() ->mapReduce($mapper, $reducer) ->all() ->toArray(); @@ -1317,8 +1346,7 @@ $reducer = function ($friends, $user, $mr) { And we supply our functions to a query: ```php -$fakeFriends = $friends->find() - ->disableHydration() +$fakeFriends = $friends->findUnhydrated() ->mapReduce($mapper, $reducer) ->all() ->toArray();