From 6620d22648d8f71944dd432436622afeb6e72093 Mon Sep 17 00:00:00 2001 From: Tigrov Date: Fri, 15 Dec 2023 11:13:49 +0700 Subject: [PATCH 1/3] Fix extensibility issue --- src/BaseActiveRecordTrait.php | 6 +-- tests/ActiveRecordTest.php | 11 +++++ .../ActiveRecord/CustomerExtraAttributes.php | 48 +++++++++++++++++++ 3 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 tests/Stubs/ActiveRecord/CustomerExtraAttributes.php diff --git a/src/BaseActiveRecordTrait.php b/src/BaseActiveRecordTrait.php index c5061b1ac..18f77d485 100644 --- a/src/BaseActiveRecordTrait.php +++ b/src/BaseActiveRecordTrait.php @@ -44,12 +44,8 @@ trait BaseActiveRecordTrait */ public function __get(string $name) { - if (isset($this->attributes[$name]) || array_key_exists($name, $this->attributes)) { - return $this->attributes[$name]; - } - if ($this->hasAttribute($name)) { - return null; + return $this->getAttribute($name); } if (isset($this->related[$name]) || array_key_exists($name, $this->related)) { diff --git a/tests/ActiveRecordTest.php b/tests/ActiveRecordTest.php index 4d054d29e..078331dcc 100644 --- a/tests/ActiveRecordTest.php +++ b/tests/ActiveRecordTest.php @@ -10,6 +10,7 @@ use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Cat; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Customer; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\CustomerClosureField; +use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\CustomerExtraAttributes; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\CustomerWithAlias; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Dog; use Yiisoft\ActiveRecord\Tests\Stubs\ActiveRecord\Item; @@ -763,4 +764,14 @@ public function testToArrayWithClosure(): void $customer->toArray(), ); } + + public function testGetExtraArrtibute() + { + $this->checkFixture($this->db, 'customer'); + + $customerQuery = new ActiveQuery(CustomerExtraAttributes::class, $this->db); + $customer = $customerQuery->findOne(1); + + $this->assertSame($customer->sex, 'm'); + } } diff --git a/tests/Stubs/ActiveRecord/CustomerExtraAttributes.php b/tests/Stubs/ActiveRecord/CustomerExtraAttributes.php new file mode 100644 index 000000000..44de67406 --- /dev/null +++ b/tests/Stubs/ActiveRecord/CustomerExtraAttributes.php @@ -0,0 +1,48 @@ + 'm', + ]; + + public function getTableName(): string + { + return 'customer'; + } + + public function getAttribute(string $name): mixed + { + if (array_key_exists($name, $this->extraAttributes)) { + return $this->extraAttributes[$name]; + } + + return parent::getAttribute($name); + } + + public function getAttributes(array $names = null, array $except = []): array + { + return array_merge(parent::getAttributes($names, $except), $this->extraAttributes); + } + + public function hasAttribute($name): bool + { + return array_key_exists($name, $this->extraAttributes) || parent::hasAttribute($name); + } +} From b2219f7eec647137680186235a1c69998d246cb5 Mon Sep 17 00:00:00 2001 From: Tigrov Date: Sat, 16 Dec 2023 11:28:26 +0700 Subject: [PATCH 2/3] Add `BaseActiveRecord::setAttributeInternal()` method --- src/BaseActiveRecord.php | 23 ++++++++++++++++------- src/BaseActiveRecordTrait.php | 14 +++----------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/BaseActiveRecord.php b/src/BaseActiveRecord.php index bdbdd4d1d..5fb7974be 100644 --- a/src/BaseActiveRecord.php +++ b/src/BaseActiveRecord.php @@ -658,13 +658,7 @@ public function save(array $attributeNames = null): bool public function setAttribute(string $name, mixed $value): void { if ($this->hasAttribute($name)) { - if ( - !empty($this->relationsDependencies[$name]) - && (!array_key_exists($name, $this->attributes) || $this->attributes[$name] !== $value) - ) { - $this->resetDependentRelations($name); - } - $this->attributes[$name] = $value; + $this->setAttributeInternal($name, $value); } else { throw new InvalidArgumentException(static::class . ' has no attribute named "' . $name . '".'); } @@ -1281,4 +1275,19 @@ public function toArray(): array } return $data; } + + /** + * Sets the named attribute value without checking if the attribute exists. + */ + protected function setAttributeInternal(string $name, mixed $value): void + { + if ( + !empty($this->relationsDependencies[$name]) + && ($value === null || $value !== $this->attributes[$name] ?? null) + ) { + $this->resetDependentRelations($name); + } + + $this->attributes[$name] = $value; + } } diff --git a/src/BaseActiveRecordTrait.php b/src/BaseActiveRecordTrait.php index 18f77d485..9d917aabb 100644 --- a/src/BaseActiveRecordTrait.php +++ b/src/BaseActiveRecordTrait.php @@ -167,10 +167,7 @@ public function __isset(string $name): bool public function __unset(string $name): void { if ($this->hasAttribute($name)) { - unset($this->attributes[$name]); - if (!empty($this->relationsDependencies[$name])) { - $this->resetDependentRelations($name); - } + $this->setAttributeInternal($name, null); } elseif (array_key_exists($name, $this->related)) { unset($this->related[$name]); } @@ -189,13 +186,8 @@ public function __unset(string $name): void public function __set(string $name, mixed $value): void { if ($this->hasAttribute($name)) { - if ( - !empty($this->relationsDependencies[$name]) - && (!array_key_exists($name, $this->attributes) || $this->attributes[$name] !== $value) - ) { - $this->resetDependentRelations($name); - } - $this->attributes[$name] = $value; + $this->setAttributeInternal($name, $value); + return; } if (method_exists($this, 'get' . ucfirst($name))) { From c802f0ac8c52ca0024aa5ff5242f82fe4e27b4bc Mon Sep 17 00:00:00 2001 From: Tigrov Date: Sat, 16 Dec 2023 11:33:24 +0700 Subject: [PATCH 3/3] Wrap with brackets `($this->attributes[$name] ?? null)` --- src/BaseActiveRecord.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BaseActiveRecord.php b/src/BaseActiveRecord.php index 5fb7974be..f4e9cee8c 100644 --- a/src/BaseActiveRecord.php +++ b/src/BaseActiveRecord.php @@ -1283,7 +1283,7 @@ protected function setAttributeInternal(string $name, mixed $value): void { if ( !empty($this->relationsDependencies[$name]) - && ($value === null || $value !== $this->attributes[$name] ?? null) + && ($value === null || $value !== ($this->attributes[$name] ?? null)) ) { $this->resetDependentRelations($name); }