Skip to content

Commit c0d58a9

Browse files
Use the connection passed to ActiveQuery result methods for schema reflection and typecasting while populating records. (#21049)
1 parent 7cf5239 commit c0d58a9

13 files changed

Lines changed: 517 additions & 31 deletions

framework/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ Yii Framework 2 Change Log
153153
- Bug #17545: Apply PostgreSQL transaction isolation levels after starting the transaction, via the new `yii\db\pgsql\Transaction` class resolved through `yii\db\Connection::$transactionMap` (terabytesoftw)
154154
- Bug #16043: Fix `yii\db\oci\Schema::findColumns()` to report `null` size for `DATE`, `TIMESTAMP` and `INTERVAL` columns instead of their internal storage byte length (terabytesoftw)
155155
- Bug #12763: Apply an explicitly configured PostgreSQL `defaultSchema` as the session `search_path` when opening the connection (terabytesoftw)
156+
- Bug #19852: Use the connection passed to `ActiveQuery` result methods for schema reflection and typecasting while populating records (terabytesoftw)
156157

157158
2.0.56 under development
158159
------------------------

framework/UPGRADE-22.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,9 @@ Notable changes include:
317317
types; `getRawValuesFromTraversableObject()` was removed.
318318
- MSSQL `PDO`, `DBLibPDO`, and `SqlsrvPDO` overrides now declare native PDO-compatible parameter and return types.
319319
- `yii\rbac\DbManager` and its new cascade extension points declare native parameter and return types.
320+
- The database connection used to fetch query results is now passed through `Query::populate()`,
321+
`ActiveQueryTrait::createModels()`, `ActiveRecord::populateRecord()`, and `ActiveRecord::getTableSchema()`. Overrides
322+
of these methods must accept the new optional `$db` parameter and forward it when calling the parent implementation.
320323

321324
Run the application's static analysis and test suite after upgrading. Pay particular attention to classes extending
322325
`Action`, `InlineAction`, `InConditionBuilder`, a database query builder, a database schema class, an MSSQL PDO wrapper,
@@ -348,6 +351,28 @@ Yii2 `22.0` adds standalone action discovery through `Module::$actionMap`, `Modu
348351

349352
## Database abstraction layer
350353

354+
### Active Record population uses the query connection
355+
356+
Passing a connection to `ActiveQuery::all()`, `one()`, `batch()`, or `each()` now uses the same connection for schema
357+
reflection and column typecasting. Previously, the rows were fetched from the supplied connection but
358+
`ActiveRecord::populateRecord()` reflected the schema through `ActiveRecord::getDb()`.
359+
360+
Custom overrides of `populateRecord()` must accept and forward the connection:
361+
362+
```php
363+
public static function populateRecord($record, $row, $db = null): void
364+
{
365+
parent::populateRecord($record, $row, $db);
366+
367+
// Custom population logic.
368+
}
369+
```
370+
371+
The attribute list still comes from `attributes()`, which reflects the schema through `getDb()` by default. A model
372+
whose table is only reachable through another connection must define that connection on the model by overriding
373+
`getDb()`, or declare its attribute list explicitly by overriding `attributes()`. The same applies to `primaryKey()`,
374+
which join deduplication and other primary-key dependent features resolve through the default schema.
375+
351376
### Composite `IN` and `NOT IN` conditions
352377

353378
`yii\db\conditions\InCondition` now normalizes `Traversable` columns and values to arrays and caches the normalized

framework/db/ActiveQuery.php

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
use yii\base\InvalidConfigException;
1212

13+
use function reset;
14+
1315
/**
1416
* ActiveQuery represents a DB query associated with an Active Record class.
1517
*
@@ -220,16 +222,18 @@ public function prepare($builder)
220222
/**
221223
* {@inheritdoc}
222224
*/
223-
public function populate($rows)
225+
public function populate($rows, $db = null)
224226
{
225227
if (empty($rows)) {
226228
return [];
227229
}
228230

229-
$models = $this->createModels($rows);
231+
$models = $this->createModels($rows, $db);
232+
230233
if (!empty($this->join) && $this->indexBy === null) {
231234
$models = $this->removeDuplicatedModels($models);
232235
}
236+
233237
if (!empty($this->with)) {
234238
$this->findWith($this->with, $models);
235239
}
@@ -244,7 +248,7 @@ public function populate($rows)
244248
}
245249
}
246250

247-
return parent::populate($models);
251+
return parent::populate($models, $db);
248252
}
249253

250254
/**
@@ -303,17 +307,20 @@ private function removeDuplicatedModels($models)
303307

304308
/**
305309
* Executes query and returns a single row of result.
306-
* @param Connection|null $db the DB connection used to create the DB command.
307-
* If `null`, the DB connection returned by [[modelClass]] will be used.
308-
* @return T|null a single row of query result. Depending on the setting of [[asArray]],
309-
* the query result may be either an array or an ActiveRecord object. `null` will be returned
310-
* if the query results in nothing.
310+
*
311+
* @param Connection|null $db the DB connection used to create the DB command. If `null`, the DB connection returned
312+
* by [[modelClass]] will be used.
313+
*
314+
* @return T|null a single row of query result. Depending on the setting of [[asArray]], the query result may be
315+
* either an array or an ActiveRecord object. `null` will be returned if the query results in nothing.
311316
*/
312317
public function one($db = null)
313318
{
314319
$row = parent::one($db);
320+
315321
if ($row !== false) {
316-
$models = $this->populate([$row]);
322+
$models = $this->populate([$row], $db);
323+
317324
return reset($models) ?: null;
318325
}
319326

framework/db/ActiveQueryTrait.php

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,24 +106,35 @@ public function with()
106106

107107
/**
108108
* Converts found rows into model instances.
109-
* @param array $rows
110-
* @return array|ActiveRecord[]
109+
*
110+
* @param array $rows The rows to be converted into model instances. Each array element represents a row of data.
111+
* @param Connection|null $db The database connection used to retrieve the rows.
112+
*
113+
* @return array|BaseActiveRecord[] The model instances created from the rows. If [[asArray]] is true, the rows
114+
* will be returned as is.
111115
* @since 2.0.11
112116
*/
113-
protected function createModels($rows)
117+
protected function createModels($rows, $db = null)
114118
{
115119
if ($this->asArray) {
116120
return $rows;
117121
} else {
118122
$models = [];
119-
/** @var ActiveRecord $class */
123+
/** @var class-string<BaseActiveRecord> $class */
120124
$class = $this->modelClass;
125+
121126
foreach ($rows as $row) {
122127
$model = $class::instantiate($row);
123-
$modelClass = get_class($model);
124-
$modelClass::populateRecord($model, $row);
128+
129+
if ($model instanceof ActiveRecord) {
130+
$model::populateRecord($model, $row, $db);
131+
} else {
132+
$model::populateRecord($model, $row);
133+
}
134+
125135
$models[] = $model;
126136
}
137+
127138
return $models;
128139
}
129140
}

framework/db/ActiveRecord.php

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -425,12 +425,19 @@ public static function tableName()
425425

426426
/**
427427
* Returns the schema information of the DB table associated with this AR class.
428-
* @return TableSchema the schema information of the DB table associated with this AR class.
428+
*
429+
* @param Connection|null $db The database connection used to retrieve the schema. If `null`, the connection
430+
* returned by [[getDb()]] will be used.
431+
*
429432
* @throws InvalidConfigException if the table for the AR class does not exist.
433+
*
434+
* @return TableSchema The schema information of the DB table associated with this AR class.
430435
*/
431-
public static function getTableSchema()
436+
public static function getTableSchema($db = null)
432437
{
433-
$tableSchema = static::getDb()
438+
$db = $db ?? static::getDb();
439+
440+
$tableSchema = $db
434441
->getSchema()
435442
->getTableSchema(static::tableName());
436443

@@ -503,15 +510,19 @@ public function transactions()
503510

504511
/**
505512
* {@inheritdoc}
513+
*
514+
* @param Connection|null $db The database connection used to retrieve the row and its schema.
506515
*/
507-
public static function populateRecord($record, $row)
516+
public static function populateRecord($record, $row, $db = null)
508517
{
509-
$columns = static::getTableSchema()->columns;
518+
$columns = static::getTableSchema($db)->columns;
519+
510520
foreach ($row as $name => $value) {
511521
if (isset($columns[$name])) {
512522
$row[$name] = $columns[$name]->phpTypecast($value);
513523
}
514524
}
525+
515526
parent::populateRecord($record, $row);
516527
}
517528

framework/db/BatchQueryResult.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ protected function fetchData()
172172

173173
$rows = $this->getRows();
174174

175-
return $this->query->populate($rows);
175+
return $this->query->populate($rows, $this->db);
176176
}
177177

178178
/**

framework/db/Query.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -272,22 +272,28 @@ public function all($db = null)
272272

273273
$rows = $this->createCommand($db)->queryAll();
274274

275-
return $this->populate($rows);
275+
return $this->populate($rows, $db);
276276
}
277277

278278
/**
279279
* Converts the raw query results into the format as specified by this query.
280-
* This method is internally used to convert the data fetched from database
281-
* into the format as required by this query.
282-
* @param array $rows the raw query result from database
283-
* @return array the converted query result
280+
*
281+
* This method is internally used to convert the data fetched from database into the format as required by this
282+
* query.
283+
*
284+
* @param array $rows The raw query result from database.
285+
* @param Connection|null $db The database connection used to retrieve the rows.
286+
*
287+
* @return array The converted query result.
284288
*/
285-
public function populate($rows)
289+
public function populate($rows, $db = null)
286290
{
287291
if ($this->indexBy === null) {
288292
return $rows;
289293
}
294+
290295
$result = [];
296+
291297
foreach ($rows as $row) {
292298
$result[ArrayHelper::getValue($row, $this->indexBy)] = $row;
293299
}

0 commit comments

Comments
 (0)