Skip to content

6.x - Allow using concrete properties for entity fields.#19313

Draft
ADmad wants to merge 17 commits into
6.xfrom
6.0-entity-props
Draft

6.x - Allow using concrete properties for entity fields.#19313
ADmad wants to merge 17 commits into
6.xfrom
6.0-entity-props

Conversation

@ADmad
Copy link
Copy Markdown
Member

@ADmad ADmad commented Mar 5, 2026

Refs #18201

@ADmad ADmad added this to the 6.0 milestone Mar 5, 2026
@ADmad ADmad force-pushed the 6.0-entity-props branch from e8f7c95 to aeca15b Compare March 5, 2026 13:53
@ADmad ADmad requested a review from Copilot March 5, 2026 13:55
@ADmad ADmad force-pushed the 6.0-entity-props branch from aeca15b to f39ab04 Compare March 5, 2026 14:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to let Cake\ORM\Entity use concrete (declared/typed) PHP properties as entity fields, while continuing to support dynamically-set fields.

Changes:

  • Refactors EntityTrait to split storage between declared properties and a new dynamicFields array.
  • Updates Entity construction to always go through patch() (removing the old “direct fields assignment” fast-path).
  • Adds a new entity fixture (UserWithProps) and a large dedicated test suite for entities using concrete properties.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 11 comments.

File Description
src/Datasource/EntityTrait.php Introduces dynamicFields + propertyFields, adds property existence checks, and rewires patch/get/unset/errors/debug behavior to support concrete properties.
src/ORM/Entity.php Removes the markClean && !useSetters early return so construction uses patch() consistently.
tests/test_app/TestApp/Model/Entity/UserWithProps.php Adds a test entity class with declared typed properties.
tests/TestCase/ORM/EntityWithConcretePropertiesTest.php Adds extensive test coverage targeting concrete-property entities (largely mirroring EntityTest).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Datasource/EntityTrait.php
Comment thread tests/TestCase/ORM/EntityWithConcretePropertiesTest.php
Comment thread tests/TestCase/ORM/EntityWithConcretePropertiesTest.php Outdated
Comment thread tests/TestCase/ORM/EntityWithConcretePropertiesTest.php Outdated
Comment thread src/Datasource/EntityTrait.php
Comment thread src/Datasource/EntityTrait.php Outdated
Comment thread src/Datasource/EntityTrait.php
Comment thread src/Datasource/EntityTrait.php Outdated
Comment thread src/Datasource/EntityTrait.php
Comment thread src/Datasource/EntityTrait.php Outdated
@ADmad ADmad force-pushed the 6.0-entity-props branch from c607910 to cdb13e9 Compare March 5, 2026 16:11
};
$entity->things = ['a', 'b'];
$entity->things[] = 'c';
$this->assertEquals(['a', 'b'], $entity->things);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a caveat of using properties through magic __get. Indirect modification of arrays won't work.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning direct reference to the property can work but it would fail if the field has our method based getter or PHP's get property hook.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this long standing behavior as well? I remember it always working this way because of the limitations you listed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, guess I never tried such indirect modification for arrays hence didn't remember. So it's not a regression and now we have test case for it :)

@ADmad ADmad marked this pull request as ready for review March 5, 2026 16:58
Comment thread src/Datasource/EntityTrait.php Outdated
Comment on lines +160 to +174
'dynamicFields',
'propertyFields',
'original',
'originalFields',
'hidden',
'virtual',
'dirty',
'new',
'errors',
'invalid',
'patchable',
'registryAlias',
'hasBeenVisited',
'requireFieldPresence',
'restrictedProperties',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way we could automate this list matching all of the properties defined on the trait? I could see us making a mistake here in the future.

Copy link
Copy Markdown
Member Author

@ADmad ADmad Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would need some type of reflection which I have avoided in general when implementing this feature. I can instead add a test case which would report a failure if this list goes out of sync with the trait properties.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added EntityWithConcretePropertiesTest::testRestrictedPropertiesMatchesTraitProperties().

I had actually missed adding accessors to the list which was caught by the test :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had actually missed adding accessors to the list which was caught by the test :)

Huzzah! Even the 🤖 s missed this.

};
$entity->things = ['a', 'b'];
$entity->things[] = 'c';
$this->assertEquals(['a', 'b'], $entity->things);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this long standing behavior as well? I remember it always working this way because of the limitations you listed.

@ADmad
Copy link
Copy Markdown
Member Author

ADmad commented Mar 7, 2026

The stan failure is because rector wants nullable properties to be initialized as null, but Entity::has() treats uninitialized vs set to null differently.

@ADmad ADmad force-pushed the 6.0-entity-props branch from c1edac2 to 9c91aa8 Compare March 10, 2026 18:17
@ADmad ADmad changed the title Allow using concrete properties for entity fields. 6.x - Allow using concrete properties for entity fields. Mar 10, 2026
@ADmad ADmad force-pushed the 6.0-entity-props branch from eb6446a to ee3b8b8 Compare April 4, 2026 13:08
- Convert $originalFields to keyed map for O(1) isset() lookups
- Simplify setOriginalField() using array_combine and += merge
- Swap branches in setHidden/setVirtual/setDirty for common path first
- Use truthy checks instead of === true/false comparisons
- Hoist $asOriginal in patch() before loop
- Pass $this->propertyFields directly in clean()
- Improve patch() phpdoc param type
@ADmad ADmad marked this pull request as draft April 11, 2026 05:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants