1818use Yiisoft \Db \Exception \NotSupportedException ;
1919use Yiisoft \Db \Expression \Expression ;
2020use Yiisoft \Db \Expression \ExpressionInterface ;
21+ use Yiisoft \Db \Query \QueryInterface ;
2122use Yiisoft \Db \Query \QueryPartsInterface ;
2223
2324use function array_diff_key ;
4849 * See {@see ActiveRecord} for a concrete implementation.
4950 *
5051 * @psalm-import-type ModelClass from ActiveQuery
52+ * @psalm-import-type RawFrom from QueryInterface
5153 */
5254abstract class AbstractActiveRecord implements ActiveRecordInterface
5355{
@@ -166,16 +168,6 @@ public function oldValue(string $propertyName): mixed
166168 return $ this ->oldValues [$ propertyName ] ?? null ;
167169 }
168170
169- /**
170- * Returns the property values that have been modified since they're loaded or saved most recently.
171- *
172- * The comparison of new and old values uses `===`.
173- *
174- * @param array|null $propertyNames The names of the properties whose values may be returned if they're changed recently.
175- * If null, {@see propertyNames()} will be used.
176- *
177- * @return array The changed property values (name-value pairs).
178- */
179171 public function newValues (array |null $ propertyNames = null ): array
180172 {
181173 $ values = $ this ->propertyValues ($ propertyNames );
@@ -330,6 +322,7 @@ public function hasProperty(string $name): bool
330322 * @return ActiveQueryInterface The relational query object.
331323 *
332324 * @psalm-param ModelClass $modelClass
325+ * @psalm-param array<string, string> $link
333326 */
334327 public function hasMany (ActiveRecordInterface |string $ modelClass , array $ link ): ActiveQueryInterface
335328 {
@@ -369,6 +362,7 @@ public function hasMany(ActiveRecordInterface|string $modelClass, array $link):
369362 * @return ActiveQueryInterface The relational query object.
370363 *
371364 * @psalm-param ModelClass $modelClass
365+ * @psalm-param array<string, string> $link
372366 */
373367 public function hasOne (ActiveRecordInterface |string $ modelClass , array $ link ): ActiveQueryInterface
374368 {
@@ -436,10 +430,8 @@ public function link(string $relationName, ActiveRecordInterface $linkModel, arr
436430
437431 if (is_array ($ via )) {
438432 [$ viaName , $ viaRelation ] = $ via ;
439- /** @var ActiveQueryInterface $viaRelation */
440433 $ viaModel = $ viaRelation ->getModel ();
441434 // unset $viaName so that it can be reloaded to reflect the change.
442- /** @var string $viaName */
443435 unset($ this ->related [$ viaName ]);
444436 } else {
445437 $ viaRelation = $ via ;
@@ -504,15 +496,19 @@ public function link(string $relationName, ActiveRecordInterface $linkModel, arr
504496 if (!$ relation ->isMultiple ()) {
505497 $ this ->related [$ relationName ] = $ linkModel ;
506498 } elseif (isset ($ this ->related [$ relationName ])) {
507- /** @psalm-var ActiveRecordInterface[] $this->related[$relationName] */
499+ /**
500+ * Related records are already an array.
501+ * @psalm-var array<string, ActiveRecordInterface[]|array[]> $this->related[$relationName]
502+ */
508503 $ indexBy = $ relation ->getIndexBy ();
509504 if ($ indexBy !== null ) {
510- if ($ indexBy instanceof Closure) {
511- $ index = $ indexBy ($ linkModel );
512- } else {
513- $ index = $ linkModel ->get ($ indexBy );
514- }
515-
505+ /**
506+ * We assume that the index is always string, int or null.
507+ * @var int|string|null $index
508+ */
509+ $ index = $ indexBy instanceof Closure
510+ ? $ indexBy ($ linkModel )
511+ : $ linkModel ->get ($ indexBy );
516512 if ($ index !== null ) {
517513 $ this ->related [$ relationName ][$ index ] = $ linkModel ;
518514 }
@@ -537,19 +533,9 @@ public function markPropertyChanged(string $name): void
537533 }
538534 }
539535
540- /**
541- * Populates an active record object using a row of data from the database/storage.
542- *
543- * This is an internal method meant to be called to create active record objects after fetching data from the
544- * database. It is mainly used by {@see ActiveQuery} to populate the query results into active records.
545- *
546- * @param array|object $row Property values (name => value).
547- */
548- public function populateRecord (array |object $ row ): void
536+ public function populateRecord (array |object $ row ): static
549537 {
550- if ($ row instanceof ActiveRecordInterface) {
551- $ row = $ row ->propertyValues ();
552- }
538+ $ row = ArArrayHelper::toArray ($ row );
553539
554540 foreach ($ row as $ name => $ value ) {
555541 $ this ->populateProperty ($ name , $ value );
@@ -558,6 +544,7 @@ public function populateRecord(array|object $row): void
558544
559545 $ this ->related = [];
560546 $ this ->relationsDependencies = [];
547+ return $ this ;
561548 }
562549
563550 public function populateRelation (string $ name , array |ActiveRecordInterface |null $ records ): void
@@ -649,12 +636,12 @@ public function set(string $propertyName, mixed $value): void
649636 * @param array $values Property values (name => value) to be assigned to the model.
650637 *
651638 * @see propertyNames()
639+ *
640+ * @psalm-param array<string, mixed> $values
652641 */
653642 public function populateProperties (array $ values ): void
654643 {
655644 $ values = array_intersect_key ($ values , array_flip ($ this ->propertyNames ()));
656-
657- /** @psalm-var mixed $value */
658645 foreach ($ values as $ name => $ value ) {
659646 $ this ->populateProperty ($ name , $ value );
660647 }
@@ -744,6 +731,8 @@ public function updateAll(array $propertyValues, array|string $condition = [], a
744731 * @throws Throwable
745732 *
746733 * @return int The number of rows updated.
734+ *
735+ * @psalm-param RawFrom|null $from
747736 */
748737 public function updateAllCounters (array $ counters , array |string $ condition = '' , array |ExpressionInterface |string |null $ from = null , array $ params = []): int
749738 {
@@ -793,6 +782,9 @@ public function updateCounters(array $counters): bool
793782 }
794783
795784 foreach ($ counters as $ name => $ value ) {
785+ /**
786+ * @psalm-suppress MixedOperand We assume that the counter value is always an integer.
787+ */
796788 $ value += $ this ->get ($ name ) ?? 0 ;
797789 $ this ->populateProperty ($ name , $ value );
798790 $ this ->oldValues [$ name ] = $ value ;
@@ -1052,6 +1044,7 @@ private function setRelationDependencies(
10521044 * @return ActiveQueryInterface The relational query object.
10531045 *
10541046 * @psalm-param ModelClass $modelClass
1047+ * @psalm-param array<string, string> $link
10551048 *
10561049 * {@see hasOne()}
10571050 * {@see hasMany()}
@@ -1111,6 +1104,8 @@ protected function deleteInternal(): int
11111104 * Defaults to `null`, meaning all changed property values will be returned.
11121105 *
11131106 * @return array The changed property values (name-value pairs).
1107+ *
1108+ * @psalm-return array<string, mixed>
11141109 */
11151110 protected function newPropertyValues (array |null $ properties = null ): array
11161111 {
@@ -1175,7 +1170,6 @@ protected function updateInternal(array|null $properties = null): int
11751170 }
11761171
11771172 $ values = $ this ->newPropertyValues ($ properties );
1178-
11791173 if (empty ($ values )) {
11801174 return 0 ;
11811175 }
@@ -1184,6 +1178,10 @@ protected function updateInternal(array|null $properties = null): int
11841178
11851179 if ($ this instanceof OptimisticLockInterface) {
11861180 $ lock = $ this ->optimisticLockPropertyName ();
1181+
1182+ /**
1183+ * @var int $lockValue We assume that optimistic lock property value is always an integer.
1184+ */
11871185 $ lockValue = $ this ->get ($ lock );
11881186
11891187 $ condition [$ lock ] = $ lockValue ;
@@ -1207,14 +1205,15 @@ protected function updateInternal(array|null $properties = null): int
12071205 return $ rows ;
12081206 }
12091207
1208+ /**
1209+ * @psalm-param array<string, string> $link
1210+ */
12101211 private function bindModels (
12111212 array $ link ,
12121213 ActiveRecordInterface $ foreignModel ,
12131214 ActiveRecordInterface $ primaryModel
12141215 ): void {
1215- /** @psalm-var string[] $link */
12161216 foreach ($ link as $ fk => $ pk ) {
1217- /** @psalm-var mixed $value */
12181217 $ value = $ primaryModel ->get ($ pk );
12191218
12201219 if ($ value === null ) {
@@ -1223,11 +1222,8 @@ private function bindModels(
12231222 );
12241223 }
12251224
1226- /**
1227- * Relation via array valued property.
1228- */
1225+ // Relation via array valued property
12291226 if (is_array ($ fkValue = $ foreignModel ->get ($ fk ))) {
1230- /** @psalm-var mixed */
12311227 $ fkValue [] = $ value ;
12321228 $ foreignModel ->set ($ fk , $ fkValue );
12331229 } else {
0 commit comments