Bug: Tracking changes in Entity
PHP Version
8.4
CodeIgniter4 Version
latest, 4.6.3
CodeIgniter4 Installation Method
Git
Which operating systems have you tested for this bug?
Linux
Which server did you use?
cli-server (PHP built-in webserver)
Database
No response
What happened?
Since it became possible to set the properties of an entity after casting, there has been a problem tracking changes to the object. The principle of operation is now based on primitives: numbers, strings, and arrays...but not objects. By setting up casting in the Model, we get a single object for attributes and original. They are links. When you change a property, it also changes in the original. If we call hasChanged(), we will not get the correct result and the object will not save these changes to the database.
The situation is complicated by nested objects like $user->profile->username->name. They must also be monitored.
In Laravel, only values are saved (mostly json). However, I think it's possible to replace an object with a similar value, and this is considered an unchanged state. This is fine for the database, but it may cause an error in the PHP code.
Steps to Reproduce
Try working with objects instead of primitives. Call hasChanged(), compare $attributes and $original values after changing objects.
Expected Output
I would like to properly track the state of an object, along with its nesting.
Anything else?
No response
Simple test code. Paste in Home.php controller. PHP 8.4.14
<?php
namespace App\Controllers;
use CodeIgniter\Entity\Entity;
class Home extends BaseController
{
public function index(): string
{
// Test #1
$user1 = (new User(['email' => '[email protected]', 'profile' => new Profile('Ivan', new Phone('+1234567890'))]))->syncOriginal();
// Iinitial object or after Db find (with casting)
d($user1->hasChanged()); // false, OK
// Change email - primitive PHP type
$user1->email = '[email protected]';
d(
$user1->hasChanged(),
$user1->hasChanged('email'),
); // true, true, OK
// Test #2
$user2 = (new User(['email' => '[email protected]', 'profile' => new Profile('Ivan', new Phone('+1234567890'))]))->syncOriginal();
// Change object
$user2->profile->name = 'Petr';
$user2->profile->phone = new Phone('+9876543210');
d(
$user2->hasChanged(),
$user2->hasChanged('profile'),
); // false, false, Fail
dd($user1, $user2);
return view('welcome_message');
}
}
readonly class Phone
{
public function __construct(public string $value)
{
}
public function __toString()
{
return $this->value;
}
}
class Profile
{
public function __construct(public string $name, public Phone $phone)
{
}
}
class User extends Entity
{
}