Skip to content

Commit d060d8a

Browse files
committed
fix: Entity::hasChanged() is unreliable for casts
1 parent 5b396db commit d060d8a

File tree

2 files changed

+95
-4
lines changed

2 files changed

+95
-4
lines changed

system/Entity/Entity.php

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ public function toRawArray(bool $onlyChanged = false, bool $recursive = false):
213213
}
214214

215215
foreach ($this->attributes as $key => $value) {
216-
if (! $this->hasChanged($key)) {
216+
if (! $this->hasChangedAttributes($key)) {
217217
continue;
218218
}
219219

@@ -254,11 +254,34 @@ public function hasChanged(?string $key = null): bool
254254
{
255255
// If no parameter was given then check all attributes
256256
if ($key === null) {
257-
return $this->original !== $this->attributes;
257+
return $this->hasChangedAttributes();
258258
}
259259

260260
$key = $this->mapProperty($key);
261261

262+
return $this->hasChangedAttributes($key);
263+
}
264+
265+
/**
266+
* Checks a attribute to see if it has changed since the entity
267+
* was created. Or, without a parameter, checks if any
268+
* attributes have changed.
269+
*
270+
* @param string|null $key key of $this->attributes
271+
*/
272+
private function hasChangedAttributes(?string $key = null): bool
273+
{
274+
// If no parameter was given then check all attributes
275+
if ($key === null) {
276+
foreach ($this->attributes as $key => $value) {
277+
if ($this->isChanged($key)) {
278+
return true;
279+
}
280+
}
281+
282+
return false;
283+
}
284+
262285
// Key doesn't exist in either
263286
if (! array_key_exists($key, $this->original) && ! array_key_exists($key, $this->attributes)) {
264287
return false;
@@ -269,7 +292,7 @@ public function hasChanged(?string $key = null): bool
269292
return true;
270293
}
271294

272-
return $this->original[$key] !== $this->attributes[$key];
295+
return $this->isChanged($key);
273296
}
274297

275298
/**
@@ -489,7 +512,6 @@ public function __get(string $key)
489512
if (method_exists($this, $method)) {
490513
$result = $this->{$method}();
491514
}
492-
493515
// Otherwise return the protected property
494516
// if it exists.
495517
elseif (array_key_exists($key, $this->attributes)) {
@@ -508,6 +530,30 @@ public function __get(string $key)
508530
return $result;
509531
}
510532

533+
/**
534+
* Get cast value from the data array.
535+
*
536+
* @return mixed|null
537+
*/
538+
private function _getCastData(string $key, array $data)
539+
{
540+
$result = null;
541+
542+
if (array_key_exists($key, $data)) {
543+
$result = $this->castAs($data[$key], $key);
544+
}
545+
546+
return $result;
547+
}
548+
549+
/**
550+
* Check if the key value is changed.
551+
*/
552+
private function isChanged(string $key): bool
553+
{
554+
return $this->_getCastData($key, $this->original) !== $this->_getCastData($key, $this->attributes);
555+
}
556+
511557
/**
512558
* Returns true if a property exists names $key, or a getter method
513559
* exists named like for __get().

tests/system/Entity/EntityTest.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,51 @@ public function testHasChangedMappedChanged()
947947
$this->assertTrue($entity->hasChanged('createdAt'));
948948
}
949949

950+
/**
951+
* @see https://github.com/codeigniter4/CodeIgniter4/issues/5905
952+
*/
953+
public function testHasChangedCastsItem()
954+
{
955+
$data = [
956+
'id' => '1',
957+
'name' => 'John',
958+
'age' => '35',
959+
];
960+
$entity = new class ($data) extends \CodeIgniter\Entity {
961+
protected $casts = [
962+
'id' => 'integer',
963+
'name' => 'string',
964+
'age' => 'integer',
965+
];
966+
};
967+
$entity->syncOriginal();
968+
969+
$entity->age = 35;
970+
971+
$this->assertFalse($entity->hasChanged('age'));
972+
}
973+
974+
public function testHasChangedCastsWholeEntity()
975+
{
976+
$data = [
977+
'id' => '1',
978+
'name' => 'John',
979+
'age' => '35',
980+
];
981+
$entity = new class ($data) extends \CodeIgniter\Entity {
982+
protected $casts = [
983+
'id' => 'integer',
984+
'name' => 'string',
985+
'age' => 'integer',
986+
];
987+
};
988+
$entity->syncOriginal();
989+
990+
$entity->age = 35;
991+
992+
$this->assertFalse($entity->hasChanged());
993+
}
994+
950995
public function testHasChangedWholeEntity()
951996
{
952997
$entity = $this->getEntity();

0 commit comments

Comments
 (0)