CodeIgniter4 icon indicating copy to clipboard operation
CodeIgniter4 copied to clipboard

feat(entity): deep change tracking for objects and arrays

Open michalsn opened this issue 2 months ago • 9 comments

Description This PR implements deep comparison for objects and arrays in Entity::hasChanged() and Entity::syncOriginal().

Previously, only shallow reference comparison was performed, meaning changes to object properties, array elements, or nested structures were not detected. This PR adds proper deep comparison using JSON normalization.

This may be considered a BC break, as existing applications that use objects within entities may now behave differently. For entities using only scalar types, there is no change in behavior.

Fixes #9777

Checklist:

  • [x] Securely signed commits
  • [x] Component(s) with PHPDoc blocks, only if necessary or adds value (without duplication)
  • [x] Unit testing, with >80% coverage
  • [x] User guide updated
  • [x] Conforms to style guide

michalsn avatar Oct 30 '25 07:10 michalsn

Nice. You can add JSON_UNESCAPED_UNICODE for readability? I'll test it later.

neznaika0 avatar Oct 30 '25 09:10 neznaika0

  1. There are problems with other classes that do not extend JsonSerializable - DateTimeInterface, DateInterval, SplFileObject, ... At least the built-in Time should preferably be included in the list.
  2. You are not using __class, __data, __enum anywhere. Do I understand correctly that this is only for tracking that not only the value has changed, but also the object?

neznaika0 avatar Oct 31 '25 16:10 neznaika0

  1. An additional option is to check the casting properties and convert it to raw format. $objectData = $this->castAs($data, $dbColumn, 'set')

neznaika0 avatar Oct 31 '25 16:10 neznaika0

  1. Good catch, I totally missed Time class... I don't know why, but I was sure it would be handled by JsonSerializable - fixed. In general, my thinking is that entities should contain DATA, not services/resources:
  • Timestamps, enums, nested entities, value objects are DATA
  • File handles, closures, DB connections, DOM objects are NOT DATA
  1. Yes, tracking changes only, nothing else.
  2. I don't think I understand the idea.

michalsn avatar Oct 31 '25 18:10 michalsn

I understand you, you stick to simplicity in many ways. I'm trying to expand possibilities by creating more complex solutions. It's not just about working with primitives. This is a better approach for a larger project than CRUD.

  1. I agree, many objects uses as services. I can't think of exact examples right now. Doctrine collection? In theory, it's a list, and it can be stored as an array in $original.

Casting converts it to primitives, so we can try using it in normalizeValue(). We will get values for json.

A small clarification: is it normal to lose microseconds? I never work with them, so I'm just thinking ahead. https://www.php.net/manual/en/datetime.constants.php#constant.date-rfc3339-extended

neznaika0 avatar Oct 31 '25 19:10 neznaika0

Good catch on the microseconds - fixed.

I've also added support for native SPL iterators. Modern collection implementations already provide a toArray() method.

I don't believe casting is the right approach in this case, since we don't require casting to be explicitly defined.

michalsn avatar Oct 31 '25 21:10 michalsn

Sad. I think casting is similar to toArray() or JsonSerializable. Because we get the object values.

Another case. Value Object (Phone, Address, Uuid...). They may not have an interface, but casting in CI4 for saving in the Model. or have the __toString() method.

neznaika0 avatar Nov 01 '25 04:11 neznaika0

Casting in our Entity implementation should be used only when seeding it with data containing scalar values. In other cases, casting should not be used.

Good call on __toString() - I added it as a fallback, when no public methods are available.

michalsn avatar Nov 01 '25 05:11 michalsn

Thanks, @michalsn for the huge PR. I don't have any questions right now. You need to use it to gain experience.

For VO, i will have to add an interface for compatibility. I think this is not a bad option.

Just to remind you, we have "nested relations" PR :smile: Maybe now it will be possible to combine it after the release of 4.7

neznaika0 avatar Nov 01 '25 06:11 neznaika0

Thank you for the review everyone!

michalsn avatar Dec 14 '25 17:12 michalsn