Problem/Motivation
Public properties on our entities allow APIs to be circumvented, and increases the likelihood of introducing bugs because an entity object may not be in a reliable or accurate state. (Additionally, ->something->value is cumbersome DX.)
We need an issue for every entity type. The content entities (user comment taxonomy_term) are the most important.
Beta phase evaluation
Issue category | Bug because properties should not be public, API methods should not be allowed to be sidestepped. |
---|---|
Issue priority | Major because this meta goes across the entire system. But each child will be a normal bug. |
Prioritized changes | Prioritized since it is a bug and it reduces fragility. |
Disruption | Somewhat disruptive for core as well as contributed and custom modules:
|
But impact will be greater than the disruption, so it is allowed in the beta.
Proposed resolution
->getSomething() will provide a proper, reliable API for each entity type.
We did this by, for each entity, opening a new issue (coping one of the ones already existing. dreditor clone patch was helpful #1803622: Add a create follow-up issue link which fills in values (clone an issue)).
http://screencast.com/t/4qPjNP8fEHVw shows one way of creating a sub issue.
Remaining tasks
- (done) Figure out how to list all Entities. See #5.
- (done) Open all issues.
- (done) Write some hints that patch producers will find helpful for creating patches for each issue.
- (still needs more helpful hints) Write review instructions with hints of what reviewers will find useful when reviewing sub issues.
User interface changes
Sub issues have no UI changes.
API changes
Sub issues have API additions, actual getter methods.
Related Issues
TBD.
Testing
Added #2527114: Create PHPUnit test that Entity class variables are protected or are allowed public to make sure that there will be no new public class variables added as a accident.
Sub-Issues
Completed tasks
Hints to patch producers (based off of #12)
Ideally the visibility of the properties which are getters and setters in this case can force people to use the property directly and instead of using it directly, use it as a method so while working on the patch you can make the visibility as protected and change them back to public before RTBC - as stated in this comment leave it protected.
Find each property in the entity and make a getter.
See #2015123: Expand NodeInterface to provide getters and setters for an example.
Please read a couple examples
the discussions in
#2016701: Expand TermInterface to provide methods
and
#2028025: Expand CommentInterface to provide methods
to see some of the common complications so you can avoid them in your issue.
do not duplicate already implemented functionality
Also (see #12),
do not add getter methods for things that already exist on EntityInterface, do not duplicate:
- id() for the primary id of the entity. id() is already doing the same thing, so do not make getId().
- getRevisionId() for the revision id (only relevant for entities that have revisions: node/custom block)
- bundle() for the bundle (e.g. the type for nodes, vocabulary for terms, ...)
- uuid()
- language()
Check if it makes sense
Only add setters if it makes sense conceptually: setId() is not necessary for database/content entities, as the ID is automatically generated for them when they are saved. Another example: setUUID(), this is set automatically initially and must not be changed afterwards.
Sometimes setters might seem to be needed, mostly in tests. Usually tests are setting something they should not. Try opening related issues (to the child) that refactor the test to not need to set something. Usually this can be done by creating a new whatever and passing in the values it needs to have at the time of creation.
Avoid conflicts
Look out for conflicts: EntityInterface for example already has isNew(). but it does something different. If making a new method, with a different purpose, pink a unique name for it.
In general
Links in the tables go to api.d.o
For example, the first config entity: Action
https://api.drupal.org/api/drupal/core%21modules%21system%21lib%21Drupal...
can see it's a class (not an interface).
It's not content entity (compare to Node)
And there is a table which shows the properties. Filter to just show properties, click on the heading Modifiers (twice) to sort and put public properties in a group at the top of that table.
For content entities, for example Node,
all of the public properties are deleted, and the init() method is deleted.
For config entities, for example Action,
the public properties should become protected.
Protected properties can be listed in the sub-issues, and considered case by case if a getter makes sense.
Initial patches in the sub-issues should not rename properties, just do a straight conversion to a getter.
Hints to patch reviewers
For each method added, check the class being extended or implemented. If a new method name is already in that class, watch out, it will be a conflict.
Please read a couple examples
the discussions in
#2016701: Expand TermInterface to provide methods
and
#2028025: Expand CommentInterface to provide methods
to see some of the common points other reviews have made and see if the issue you are reviewing can use similar feedback.
check for duplicated functionality
getter methods should not have been added for things that already exist on EntityInterface. For example, these should not have been implemented
- getId() should not have been added, since primary id of the entity can be gotten with the already existing id()
- read the classes that are being extended or implemented to see if there might have been methods to use that serve the same purpose (even if the name might be a little different).
Check if it makes sense
Setters should only have been added if it makes sense conceptually: setId() is not necessary for database/content entities, as the ID is automatically generated for them when they are saved. Another example: setUUID() is not needed, this is set automatically initially and must not be changed afterwards.
Check for conflicts
Look out for conflicts: EntityInterface for example already has isNew(). but it does something different. If making a new method, with a different purpose, the two will need unique names. It might take some thought to name things by their purpose.
Comment | File | Size | Author |
---|---|---|---|
#5 | class-list.png | 226.84 KB | YesCT |
#5 | class-ng-properties.png | 289.77 KB | YesCT |
#5 | ng.png | 110.79 KB | YesCT |
Comments
Comment #1
YesCT CreditAttribution: YesCT commentedI'm not sure about the tags for this and the sub issues.
Will the getters we want just be for properties or other things too?
I'm going to create a few issues.
I'll try and figure out which ones are NG.
grep -R "extends EntityNG" *
gives
grep -R "extends Entity " *
Is that an ok way to find all Entities?
Comment #1.0
YesCT CreditAttribution: YesCT commenteddont list each property in the summary of sub-issues.. just the patch for that. :) -c
Comment #2
YesCT CreditAttribution: YesCT commentedalternative to get a list (hint from dawehner): phpstorm, command-o entityinterface, control-h to show hierarchy
http://screencast.com/t/bsHHvjGpw
EntityInterface (core/lib/Drupal/Core/Entity/EntityInterface.php)
ConfigEntityInterface (core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php)
ActionConfigEntityInterface (core/modules/system/lib/Drupal/system/ActionConfigEntityInterface.php)
Action (core/modules/system/lib/Drupal/system/Plugin/Core/Entity/Action.php)
BlockInterface (core/modules/block/lib/Drupal/block/BlockInterface.php)
Block (core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php)
BreakpointGroupInterface (core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointGroupInterface.php)
BreakpointGroup (core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/BreakpointGroup.php)
BreakpointInterface (core/modules/breakpoint/lib/Drupal/breakpoint/BreakpointInterface.php)
Breakpoint (core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/Breakpoint.php)
CategoryInterface (core/modules/contact/lib/Drupal/contact/CategoryInterface.php)
Category (core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php)
ConfigEntityBase (core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php)
Action (core/modules/system/lib/Drupal/system/Plugin/Core/Entity/Action.php)
Block (core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php)
Breakpoint (core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/Breakpoint.php)
BreakpointGroup (core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/BreakpointGroup.php)
Category (core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php)
ConfigTest (core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php)
ConfigQueryTest (core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigQueryTest.php)
CustomBlockType (core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlockType.php)
DisplayBase (core/modules/layout/lib/Drupal/layout/Config/DisplayBase.php)
Display (core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php)
UnboundDisplay (core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php)
Editor (core/modules/editor/lib/Drupal/editor/Plugin/Core/Entity/Editor.php)
EntityDisplayBase (core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php)
EntityDisplay (core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php)
EntityFormDisplay (core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityFormDisplay.php)
EntityViewMode (core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityViewMode.php)
Field (core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php)
FieldInstance (core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php)
FilterFormat (core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php)
ImageStyle (core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php)
Menu (core/modules/system/lib/Drupal/system/Plugin/Core/Entity/Menu.php)
PictureMapping (core/modules/picture/lib/Drupal/picture/Plugin/Core/Entity/PictureMapping.php)
Role (core/modules/user/lib/Drupal/user/Plugin/Core/Entity/Role.php)
Shortcut (core/modules/shortcut/lib/Drupal/shortcut/Plugin/Core/Entity/Shortcut.php)
Tour (core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/Tour.php)
View (core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php)
Vocabulary (core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php)
ConfigTestInterface (core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTestInterface.php)
ConfigTest (core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php)
ConfigQueryTest (core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigQueryTest.php)
CustomBlockTypeInterface (core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeInterface.php)
CustomBlockType (core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlockType.php)
DisplayInterface (core/modules/layout/lib/Drupal/layout/Config/DisplayInterface.php)
BoundDisplayInterface (core/modules/layout/lib/Drupal/layout/Config/BoundDisplayInterface.php)
Display (core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php)
DisplayBase (core/modules/layout/lib/Drupal/layout/Config/DisplayBase.php)
Display (core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php)
UnboundDisplay (core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php)
UnboundDisplayInterface (core/modules/layout/lib/Drupal/layout/Config/UnboundDisplayInterface.php)
UnboundDisplay (core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php)
EditorInterface (core/modules/editor/lib/Drupal/editor/EditorInterface.php)
Editor (core/modules/editor/lib/Drupal/editor/Plugin/Core/Entity/Editor.php)
EntityDisplayBaseInterface (core/modules/entity/lib/Drupal/entity/EntityDisplayBaseInterface.php)
EntityDisplayBase (core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php)
EntityDisplay (core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php)
EntityFormDisplay (core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityFormDisplay.php)
EntityDisplayInterface (core/modules/entity/lib/Drupal/entity/EntityDisplayInterface.php)
EntityDisplay (core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php)
EntityFormDisplayInterface (core/modules/entity/lib/Drupal/entity/EntityFormDisplayInterface.php)
EntityFormDisplay (core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityFormDisplay.php)
EntityViewModeInterface (core/modules/entity/lib/Drupal/entity/EntityViewModeInterface.php)
EntityViewMode (core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityViewMode.php)
FieldInstanceInterface (core/modules/field/lib/Drupal/field/FieldInstanceInterface.php)
FieldInstance (core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php)
FieldInterface (core/modules/field/lib/Drupal/field/FieldInterface.php)
Field (core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php)
FilterFormatInterface (core/modules/filter/lib/Drupal/filter/FilterFormatInterface.php)
FilterFormat (core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php)
ImageStyleInterface (core/modules/image/lib/Drupal/image/ImageStyleInterface.php)
ImageStyle (core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php)
MenuInterface (core/modules/system/lib/Drupal/system/MenuInterface.php)
Menu (core/modules/system/lib/Drupal/system/Plugin/Core/Entity/Menu.php)
PictureMappingInterface (core/modules/picture/lib/Drupal/picture/PictureMappingInterface.php)
PictureMapping (core/modules/picture/lib/Drupal/picture/Plugin/Core/Entity/PictureMapping.php)
RoleInterface (core/modules/user/lib/Drupal/user/RoleInterface.php)
Role (core/modules/user/lib/Drupal/user/Plugin/Core/Entity/Role.php)
ShortcutInterface (core/modules/shortcut/lib/Drupal/shortcut/ShortcutInterface.php)
Shortcut (core/modules/shortcut/lib/Drupal/shortcut/Plugin/Core/Entity/Shortcut.php)
TourInterface (core/modules/tour/lib/Drupal/tour/TourInterface.php)
Tour (core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/Tour.php)
ViewStorageInterface (core/modules/views/lib/Drupal/views/ViewStorageInterface.php)
View (core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php)
ViewUI (core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php)
VocabularyInterface (core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyInterface.php)
Vocabulary (core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php)
ContentEntityInterface (core/lib/Drupal/Core/Entity/ContentEntityInterface.php)
CommentInterface (core/modules/comment/lib/Drupal/comment/CommentInterface.php)
Comment (core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php)
CustomBlockInterface (core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockInterface.php)
CustomBlock (core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php)
FeedInterface (core/modules/aggregator/lib/Drupal/aggregator/FeedInterface.php)
Feed (core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php)
FileInterface (core/modules/file/lib/Drupal/file/FileInterface.php)
File (core/modules/file/lib/Drupal/file/Plugin/Core/Entity/File.php)
ItemInterface (core/modules/aggregator/lib/Drupal/aggregator/ItemInterface.php)
Item (core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php)
MenuLinkInterface (core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php)
MenuLink (core/modules/menu_link/lib/Drupal/menu_link/Plugin/Core/Entity/MenuLink.php)
NodeInterface (core/modules/node/lib/Drupal/node/NodeInterface.php)
Node (core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php)
TermInterface (core/modules/taxonomy/lib/Drupal/taxonomy/TermInterface.php)
Term (core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php)
Entity (core/lib/Drupal/Core/Entity/Entity.php)
ConfigEntityBase (core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php)
Action (core/modules/system/lib/Drupal/system/Plugin/Core/Entity/Action.php)
Block (core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php)
Breakpoint (core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/Breakpoint.php)
BreakpointGroup (core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/BreakpointGroup.php)
Category (core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php)
ConfigTest (core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php)
ConfigQueryTest (core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigQueryTest.php)
CustomBlockType (core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlockType.php)
DisplayBase (core/modules/layout/lib/Drupal/layout/Config/DisplayBase.php)
Display (core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php)
UnboundDisplay (core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php)
Editor (core/modules/editor/lib/Drupal/editor/Plugin/Core/Entity/Editor.php)
EntityDisplayBase (core/modules/entity/lib/Drupal/entity/EntityDisplayBase.php)
EntityDisplay (core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php)
EntityFormDisplay (core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityFormDisplay.php)
EntityViewMode (core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityViewMode.php)
Field (core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php)
FieldInstance (core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php)
FilterFormat (core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php)
ImageStyle (core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php)
Menu (core/modules/system/lib/Drupal/system/Plugin/Core/Entity/Menu.php)
PictureMapping (core/modules/picture/lib/Drupal/picture/Plugin/Core/Entity/PictureMapping.php)
Role (core/modules/user/lib/Drupal/user/Plugin/Core/Entity/Role.php)
Shortcut (core/modules/shortcut/lib/Drupal/shortcut/Plugin/Core/Entity/Shortcut.php)
Tour (core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/Tour.php)
View (core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php)
Vocabulary (core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php)
EntityCacheTest (core/modules/system/tests/modules/entity_cache_test_dependency/lib/Drupal/entity_cache_test_dependency/Plugin/Core/Entity/EntityCacheTest.php)
EntityNG (core/lib/Drupal/Core/Entity/EntityNG.php)
Comment (core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php)
CustomBlock (core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php)
EntityTest (core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php)
EntityTestDefaultAccess (core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestDefaultAccess.php)
EntityTestLabel (core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestLabel.php)
EntityTestLabelCallback (core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestLabelCallback.php)
EntityTestMul (core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMul.php)
EntityTestNoLabel (core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestNoLabel.php)
EntityTestRender (core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestRender.php)
EntityTestRev (core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestRev.php)
EntityTestMulRev (core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMulRev.php)
Feed (core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php)
Item (core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php)
Node (core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php)
Term (core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php)
User (core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php)
FieldUITestNoBundle (core/modules/field_ui/tests/modules/field_ui_test/lib/Drupal/field_ui_test/Plugin/Core/Entity/FieldUITestNoBundle.php)
File (core/modules/file/lib/Drupal/file/Plugin/Core/Entity/File.php)
MenuLink (core/modules/menu_link/lib/Drupal/menu_link/Plugin/Core/Entity/MenuLink.php)
Message (core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Message.php)
TestEntity (core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/TestEntity.php)
BundleKeyTestEntity (core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleKeyTestEntity.php)
BundleTestEntity (core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleTestEntity.php)
CacheableTestEntity (core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/CacheableTestEntity.php)
EntityBCDecorator (core/lib/Drupal/Core/Entity/EntityBCDecorator.php)
UserBCDecorator (core/modules/user/lib/Drupal/user/UserBCDecorator.php)
MessageInterface (core/modules/contact/lib/Drupal/contact/MessageInterface.php)
Message (core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Message.php)
UserInterface (core/modules/user/lib/Drupal/user/UserInterface.php)
User (core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php)
UserBCDecorator (core/modules/user/lib/Drupal/user/UserBCDecorator.php)
Comment #2.0
YesCT CreditAttribution: YesCT commentedlist the entities from the grep
Comment #2.1
YesCT CreditAttribution: YesCT commentedadded term issue
Comment #2.2
YesCT CreditAttribution: YesCT commentedadded link to video of creating sub issue
Comment #3
YesCT CreditAttribution: YesCT commentedTrying to write some hints to patch producers...
Looking at the Node issue, patch from #7 in there #2015123-7: Expand NodeInterface to provide getters and setters
has a section that begins like:
So, I'm thinking of looking at similar files and using the @var list to suggest what getters we should make.
Comment #3.0
YesCT CreditAttribution: YesCT commentedupdated remaining tasks.
Comment #4
YesCT CreditAttribution: YesCT commentedNo @var's in Term, so maybe didn't need that issue.
I'll take a break from this and come back to make some sub issues tomorrow.
Comment #5
YesCT CreditAttribution: YesCT commentedInterfaces wont have properties. Look at *classes*.
From https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21...
The list of classes is under "Implemented by"
Looking at the first one, action
https://api.drupal.org/api/drupal/core%21modules%21system%21lib%21Drupal...
can see it's a class (not an interface).
It's not NG (compare to Node)
And there is a table which shows the properties. Filter to just show properties, click on the heading Modifiers (twice) to sort and put public properties in a group at the top of that table.
Node https://api.drupal.org/api/drupal/core%21modules%21node%21lib%21Drupal%2...
shows it's NG.
For NG entities, for example Node,
all of the public properties are deleted, and the init() method is deleted.
For non-NG entities, for example Action,
the public properties should become protected.
Protected properties can be listed in the sub-issues, and considered case by case if a getter makes sense.
Initial patches in the sub-issues should not rename properties, just do a straight conversion to a getter.
----
Taking out Base and BC from the list yeilds:
Action
Block
Breakpoint
BreakpointGroup
Category
Comment
ConfigTest
CustomBlock
CustomBlockType
Display
Editor
Entity
EntityDisplay
EntityFormDisplay
EntityViewMode
Feed
Field
FieldInstance
File
FilterFormat
ImageStyle
Item
Menu
MenuLink
Message
Node
PictureMapping
Role
Shortcut
Term
Tour
UnboundDisplay
User
View
ViewUI
Vocabulary
Comment #6
tim.plunkettComment #6.0
tim.plunkettUpdated issue summary.
Comment #6.1
YesCT CreditAttribution: YesCT commentedbetter list of entity types
Comment #7
XanoAre we replacing public property access by getters and setters because it's cumbersome, or because property access is tight coupling and we don't want that? In case of the latter, we will also need to convert entity_create() calls to no longer provide a second argument, and we can only allow ourselves to access properties from within the entity class itself.
Comment #8
BerdirWoah, I didn't know this issue :) Changing title to *methods*, it's not just getters.
Contact Message and File are NG and are done, the NG conversion patch already added an interface.
User is in #2026347: Adding methods to UserInterface/AccountInterface. Will update the summary asap.
@Xano: The reason we remove the public properties for NG entities is that they're not actually public, they're just a hack to get autocomplete when we type hint on the class. There's the crazy init() method that then unset()'s them, so that __get() is invoked. Which we stop doing (type hint on classes), so that hack is useless.
That said, the methods are for convenience. It's still valid to pass values to entity_create() and access them directly. NG exposes it's fields dynamically and you can generically access them, if you want to work with entities in a generic way. That's the whole point of it's existence. Rest, Rules, edit.module, translation management, ... If you specifically access nodes and type hint on NodeInterface, it's recommended to use the methods.
Comment #8.0
Berdirupdated hints to patch producers encorporating info from #5
Comment #8.1
BerdirUpdated issue summary.
Comment #9
BerdirUpdated change notice a bit.
I think the separation should be database vs config entities, not NG vs. not NG.
Also note that
The second line is not an API *change*, as accessing it will still work, the public properties are just a hack for autocomplete, as described above. Access already goes through __get().
While..
the second line here is an API *change*, $action->something will no longer work. So we can still add the interface methods any time we want, but changing the properties to protected is a different thing.
Comment #10
BerdirComment #10.0
BerdirFix markup for File
Comment #10.1
BerdirUpdated issue summary.
Comment #10.2
BerdirUpdated issue summary.
Comment #11
BerdirAh, instead of creating the issues myself, I'm just going to tag this as Novice directly :)
So if all issues for the NG entities already have issues, feel free to create additional ones for the second group of entities.
Comment #11.0
BerdirUpdated issue summary.
Comment #11.1
alarcombe CreditAttribution: alarcombe commentedAdded correct link to Expand ItemInterface (aggregator.module) with methods
Comment #11.2
YesCT CreditAttribution: YesCT commentedadded action issue
Comment #11.3
plopescCreating new class follow-up issues
Comment #12
BerdirNote:
* Do not add getter methods for things that already exist on EntityInterface, do not duplicate:
** id() for the primary id of the entity (no getId())
** getRevisionId() for the revision id (only relevant for entities that have revisions: node/custom block)
** bundle() for the bundle (e.g. the type for nodes, vocabulary for terms, ...)
** uuid()
** language()
* Only add setters if it makes sense conceptually: setId() is not necessary for database/content entities, as the ID is automatically generated for them when they are saved. Another example: setUUID(), this is set automatically initially and must not be changed afterwards.
* Look out for conflicts: EntityInterface for example already has isNew().
When in doubt, look at some other issues of similar entity types and discuss with a person nearby :)
Comment #12.0
BerdirAdding new follow-up issues
Comment #12.1
YesCT CreditAttribution: YesCT commentedupdated with hints.
Comment #12.2
YesCT CreditAttribution: YesCT commentedoops wrong comment number.
Comment #13
Wim LeersSo… #9 suggests that this should not happen to config entities. Yet #2030605: Expand Editor with methods exists, while
Editor
is a config entity. Can somebody please clarify what is going on here?I agree that
is cumbersome, but that's not actually what happens for config entities (or at least not for
Editor
).Comment #14
tim.plunkett#9 is just saying lets not make them protected.
We should still add methods and never use the properties directly.
For purposes of the patch it would be helpful to make them all protected to make sure the patch works, then switch them back to public before RTBC.
Comment #15
BerdirYes, what @tim.plunkett said, we type hint on interfaces, that means you don't see the public properties of the class and we need methods :)
Comment #15.0
Berdirorganizing
Comment #16
Wim Leers#14 & #15: okay. I've done this at #2030605-2: Expand Editor with methods now. But… I still fail to see *why* we need to do this in the first place for config entities. The config system requires that all properties are public, so nothing prevents developers from using the public properties rather than the accessor methods. So now you have two ways to do the exact same thing. Which is inferior to what we have now.
So: why?
Comment #17
XanoWhat about \Drupal\Core\Entity\EntityInterface::getExportProperties()? That, in combination with entity type-specific protected properties, works fine.
Comment #18
tim.plunkettYeah that's 100% wrong. The only properties required to be public are $id and $uuid, and that's for stupid reasons anyway.
See all of the properties on \Drupal\views\Plugin\Core\Entity\View, and the getExportProperties() at the bottom.
Comment #19
tim.plunkettSee also #1301106: Rely on methods to access entity properties [policy, no patch]
Comment #20
Ivan Zugec CreditAttribution: Ivan Zugec commentedQuick question, should patches for this meta issue have tests? (I asked the same question in #2030641: Expand FilterFormat with methods)
It looks like some do and some don't.
For example, the following patches do not have tests:
- #2015123: Expand NodeInterface to provide getters and setters
- #2026347: Adding methods to UserInterface/AccountInterface
And the following do have patches:
- #1983548: Convert contact message entities to the new Entity Field API
- #1818568: Convert files to the new Entity Field API
Comment #21
XanoYes, we should have full code coverage.
Comment #21.0
Xanoadded @ to some issues
Comment #22
kgoel CreditAttribution: kgoel commentedCan someone please confirm if getters and setters need to be protected and change visibility into public before RTBC as stated here - https://drupal.org/node/2016679#comment-7616179
Comment #23
johnennew CreditAttribution: johnennew commentedI'm going to go for setting the parameters as protected for ConfigEntityBase Entities as @tim.plunkett suggests in #18. This requires an overridden version of \Drupal\Core\Config\Entity\ConfigEntityBase::getExportProperties() function on the Entity copying the pattern in the view Entity.
Note the issue description describes this.
Comment #24
kristofferwiklund CreditAttribution: kristofferwiklund commentedI did some work with Breakpoint #2030585: Expand Breakpoint with methods and wrote some tests. And I was thinking should we have some common test file to making sure that the different entity classes does not define methods mention in #12. So a simple "if function exist" test for every entity? This should be possible to write in PHPUnit and should go fast.
Comment #24.0
kristofferwiklund CreditAttribution: kristofferwiklund commentedUpdated issue summary.
Comment #25
sunWhy didn't we make all of the new setter methods chainable?
Comment #26
sunFurther diving into the example snippet of #25:
#2015123: Expand NodeInterface to provide getters and setters added:
However,
getTitle()
duplicatesEntityInterface::label()
.In turn, there are three Node entity methods now:
Since the whole point of this effort is DX, it would be good for DX to ensure consistency and sanity.
Comment #27
vijaycs85Cleaning up sub-tasks...
Comment #28
webchickI suppose this needs to be a beta blocker (either to finish the open patches, or to roll back the ones committed so far). It makes no sense to ship Drupal 8 with half the entities having raw properties and the other half having accessor methods; that's a recipe for total confusion.
The issue summary could do with a much better description of why we would spear-head such a massive API change post-API freeze ("->getSomething() would be nicer." doesn't quite cut it. ;)). We're 7 months into it and not half done yet. Not looking good.
Comment #29
BerdirI don't have time right now to update the issue summary in detail, but if someone wants to work on it, here are some reasons and explanations.
We decided to use interfaces for entity objects whenever possible and type hint against those. Interfaces can not define class properties (public or not), only methods.
Assuming you're using an IDE with method autocomplete (PhpStorm, Netbeans, .. and if you're not, you should), go to a method that has a "NodeInterface $node" argument, write $node-> and you will see suggestions like getTitle(), isPublished() and so on.
Now do the same for TermInterface $term, and all you get is the generic methods from ContentEntityInterface and upwards. You don't see $term->name for example, because that's defined on Term, not TermInterface.
So, without specific methods, those interfaces are not half as useful as they could be.
Now, for content entities, everything is a field. You would have to write $node->title->value, not $node->title to access the title. And not doing so can result in pretty weird errors if you pass that to a function that expects a string or so.
Additionally, content entities do not actually work with public properties anymore, @fago came up with this crazy trick to define them and then in __construct() and __wakeup(), call $this->init(), which does an unset($this->name) and so on. The only reason to do that was to be able to get autocomplete for $term->name, which no longer works, as demonstrated above. That's why we want to remove it in #2095919: Kill ContentEntityBase::init() . And the idea was to wait on the remaining content issues (feed, comment, term) which replace those public properties with methods. We could alternatively just drop the remaining properties there, being able to remove that trick/method is my main motivation for doing this at the moment.
That said, at least for content entities, this is not an API change. $node->title->value still works. And there are no methods for configurable fields, there is no $node->getBody(), only $node->body->value. So there will always be a certain amount of inconsistencies, but that's IMHO not a reason to not do this at all.
For config entities, it's not an API change as long as we leave the public properties and don't change them to protected. Which I think we shouldn't do at this point. They don't have the additional problems that content entities have, so are IMHO not as important.
I also don't see why the inconsistency between different entity objects is such a problem. All those config entities are already way more unified than they were in 7.x, with a common API to load and save them. Moving from accessing properties to methods defined on interfaces and from functions to classes is an ongoing effort everywhere in 8.x, we're just trying to catch up with everyone else here.
So in my opinion, this is not a beta blocker. The only thing that I could possibly see as a beta blocker is the mentioned kill init() issue, because it's going to be very confusing if people pick the wrong entity type as an example, one that still relies on it. And we can do that without the three remaining content entity issues, if we have to, as described.
Comment #30
xjmAha, this is one of the "missing Entity DX beta blockers" that I keep talking about. It's at least a beta target; it has significant enough impact on DX that we should try to finish beforehand.
Edit: I'm open to discuss it being a major beta target instead, but I'd really like to see the "expected" Entity API that will be used all over modules everywhere available by beta.
Comment #31
xjmDiscussed with @catch. Let's do this.
These are API additions for the most part, not BC breaks unless you are
subclassing Nodecreating your own special flowerMyNode implements NodeInterface
or something, I think. Let's tag the child issues "API addition", "DX", and "beta target" accordingly.Comment #32
xjmAlso, catch pointed out that we should stop doing these if they are not done by RC (maybe we could add them in 8.1.0 but not 8.0.1) but that they can probably keep happening after beta. Just it would be ideal if we got as much possible in before then for contrib DX.
Comment #33
xjmComment #34
YesCT CreditAttribution: YesCT commentednot adding all the sub issues. just some related ones.
Comment #35
catchSo the only way this would be a BC break is the following (unless I missed something):
1. You are completely implementing a replacement Node class from scratch, implementing NodeInterface.
2. You are subclassing Node, and the implementations on the Node class aren't compatible with your implementation (because if they were compatible, then even this is not an API change).
Both #1 and #2 have minimal effect, and if a contrib module was affected, it's a very simple change to make.
For code interacting with the interface, rather than implementing it, it's only an API addition.
Comment #36
webchickRight, it's technically only an API addition from a procedural point of view. But from a DX point of view, if half of the entities have accessor methods and the other half don't, it's going to create a very confusing situation, which is not what we want when we're trying to "complete" APIs.
I'm comfortable with major + beta target. Thanks to Berdir for the expanded reasoning in #29.
Comment #37
BerdirIn case of content entities and in combination with #2095919: Kill ContentEntityBase::init() , what could happen is that you take Term (Node is already changed), as an example, copy it for your own new entity type, adjust the public properties and change your init() implementation and then we remove the call to that method and very interesting things (tm) will happen. But that is also easy to fix by removing the properties and the method (there is no requirement to provide methods).
Comment #38
fagoYeah, #2095919: Kill ContentEntityBase::init() makes sense even as beta blocker to me as it might confuse people.
True, but we won't have all of core following best practices by beta. So if some entities use less methods than others, that's not ideal but not a big deal imho - as long as there is consistent usage of patterns for content & config entities.
Comment #39
daffie CreditAttribution: daffie commentedComment #40
daffie CreditAttribution: daffie commentedRemoved #2030599: Expand Display with methods from the list, because it has been removed from core to its own module.
Comment #41
daffie CreditAttribution: daffie commentedComment #42
daffie CreditAttribution: daffie commentedMoved issues #2028035: Expand CustomBlockInterface with methods and #2030643: Expand ImageStyle with methods to the completed section.
Comment #43
benjy CreditAttribution: benjy commentedThis makes perfect sense to me, it's basic encapsulation which, is good here because it hides the detail from the user that you actually have to access the "value" property to get the "value".
I was trying to think about it from a new comers point of view and how I would go about it presuming i'd come straight from D7. I think some code might illustrate it better, (Note, I used node because it was simple even though it has getters now).
I'm not entirely sure how you'd figure out that you had to do $node->title->value.
EDIT: I can't seem to get any white space into my code sample...
Comment #44
sunJust to re-complete the previously raised considerations:
#26 still applies today and presents a major inconsistency both in terms of API and DX:
$node->title->value
==$node->getTitle()
!=$entity->label()
$node->setTitle()
== ?(1) is not even fully true anymore today, in case you consider the use-case of rendering a node's title (label) for theming purposes, because title is a field now.
Next to completing the big picture of entity type specific interface methods (good idea), we should also ensure consistency between content and config entities.
Ideally,
getLabel()
andsetLabel()
. Let's make at least the shared base entity properties consistent for all entities?Speaking of
getLabel()
, let's bear in mind that our currentid()
,uuid()
,label()
, etc methods are lacking a "get" prefix, which brutally stems from D7/PHP4-era thinking. In OO, all methods without a "get", "set", "has", or "is" prefix are supposed to do something beyond a trivial operation.Despite following common OO practices, D8 requires you to learn that a seemingly arbitrary method
foo()
actually just returns something crucial.In terms of consistency, industry standards, and learnability, I could not agree more; all of these (API) changes should be performed prior to releasing Drupal 8.0.
Comment #45
Wim LeersThanks so much for the clarifications. I'll ensure #2030605: Expand Editor with methods happens.
Comment #46
daffie CreditAttribution: daffie commentedIn the issue summery is a difference between NG and non-NG entities.
- Can somebody explain what NG stands for?
- Is the difference between NG and non-NG still relevant?
- If it is not relevant any more can I remove it from the issue summery?
This issue has been given a priority for major and tagged with "beta target" and "rc deadline".
- Does that mean that related sub-issues are also priority major and tagged with "beta target" and "rc deadline"?
Comment #47
XanoNG stood for Next Generation which (mostly) was about typed data for entities. The backwards compatibility layer was removed a couple of months ago and as far as I know the concept of NG no longer exists in core.
Comment #48
BerdirNG is no longe relevant, but what this actually is and always was is the separation between content (NG) and config entities.
Comment #49
daffie CreditAttribution: daffie commentedSo the NG and the non-NG stuff can be removed from the issue summery?
Comment #50
BerdirNo, but replaced with Content/Config.
Comment #51
daffie CreditAttribution: daffie commentedComment #52
daffie CreditAttribution: daffie commented@Berdir: Thanks for clearing that up.
I have updated the issue summery with the content/config part.
This issue has been given a priority for major and tagged with "beta target" and "rc deadline".
- Does that mean that related sub-issues are also priority major and tagged with "beta target" and "rc deadline"?
Comment #53
tim.plunkettOnce #2224833: Remove redundant ID manipulation in ConfigEntityStorage::save() goes in, then all of the
public $id
(or whatever key it is) can be made protected. We can revisit the entity types that were already converted, but the new conversions can make this change in their dedicated issue.Comment #54
Wim LeersIt would be nice if this issue explained that your entity type has to implement
toArray()
once you switch the properties toprotected
. Just wasted quite a bit of time on debugging while working on #2030605: Expand Editor with methods.Comment #55
xjmPer discussion with @Berdir, the most important task to move these issues forward is architectural review of the child issues.
Edit: Also, it'd be awesome if someone could incorporate the explanations on-issue of what the purpose of all this is, that'd be valuable. Suggestions on what to look for in a code review would also be valuable.
Comment #56
xjmComment #57
YesCT CreditAttribution: YesCT commentedI opened #2246695: replace all core usages of id() with getid() just to clarify the discussion about using id() instead of getId().
Comment #58
justafishComment #59
daffie CreditAttribution: daffie commentedComment #60
Sharique CreditAttribution: Sharique commentedThere is no separate issue for terms, so should it be as part of #2030669 - Expand Vocabulary with methods, or create a separate issue? I prefer to do on same issue, what other's have to say?
Comment #61
BerdirTerms were done in #2016701: Expand TermInterface to provide methods, what else would you want to do there?
Comment #62
Sharique CreditAttribution: Sharique commentedOops, I miss that.
Fields are missing in Drupal\taxonomy\Entity\Term class.
Comment #63
mgiffordComment #64
kristiaanvandeneyndeComment #65
Mile23Just want to point out that most of the config entity issues linked from the original post have some work on them, and are awaiting review and maybe even RTBC. :-)
#2030595: Expand ConfigTest with methods and #2030637: Expand FieldConfig/BaseFieldOverride/FieldConfigBase with methods might be 'won't fix' at this point.
Comment #66
YesCT CreditAttribution: YesCT commentedwhen we first opened this meta, we planned to keep the properties public, .. since then we realized how important it is to have the properties protected and use methods instead. I think we realize this even more now. in #2030645-31: Expand Menu with methods @alexpott reclassified it as a bug, and explained why these changes are allowed in the beta.
updating the issue summary with that information.
Comment #67
xjmUpdated the "disruption" section with specifics (please double-check them).
Comment #68
xjmComment #69
xjmComment #70
daffie CreditAttribution: daffie commentedMoved #2030645: Expand Menu with methods to the completed tasks list
Comment #71
yched CreditAttribution: yched commentedUpdated the IS for the new names of Field config entities (FieldStorageConfig, FieldConfig, BaseFieldOverride)
Comment #72
daffie CreditAttribution: daffie commentedThe module custom_block has been changed to block_content.
Comment #73
daffie CreditAttribution: daffie commentedThis is what alexpott said in #2030645: Expand Menu with methods comment #31:
The problem is that the following Config Entities have variables that are public and they are on the are on the completed tasks list:
- NodeType
- FilterFormat
- ShortcutSet
- Migration
- RDFMapping
- ImageStyle
- Searchpage
- Action
- ConfigurableLanguage
If we want to do what alexpott is saying we need to update them. Do we open new issues and attach them to this meta issue or do we need to solve this some other way?
Comment #74
daffie CreditAttribution: daffie commentedThe issues #2030613: Expand EntityViewMode (really EntityDisplayModeBase) with methods and #2030661: Expand Tour with methods have landed.
Comment #75
daffie CreditAttribution: daffie commentedThe issue is all about encapsulation. One of the pillars of object-oriented programming. As a community we must get this fixed for drupal core. The problem is that patches made for the sub-issues are open for a long time. So I would like to make a proposition. If you write a patch for a sub-issue, then I will do a good review. Hopefully gets this issue solved before the first release candidate. At the moment there are 9 sub-issues left and probable 9 more to follow. So who will write a patch.
Comment #76
YesCT CreditAttribution: YesCT commented@daffie in #73
There was a time we would add on little follow-ups to existing issues, but we dont anymore.
Make new issues. (note dreditor has a clone issue button you might find helpful)
Make the parent of them this issue. Make them "related" to the issue that added the methods but left the properties protected.
Comment #77
daffie CreditAttribution: daffie commented@YesCT: I will make new issues for them tomorrow. Thank you for the help. If you can help with some "novice" patch makers, I shall do the reviews. I would be really nice if we would finish this issue before the first release candidate.
Comment #78
daffie CreditAttribution: daffie commentedComment #79
daffie CreditAttribution: daffie commentedComment #80
Mile23I added this: #2396713: Figure out how to encapsulate writable settings supplied by DataDefinitionInterface.
it's blocking this: #2030637: Expand FieldConfig/BaseFieldOverride/FieldConfigBase with methods, and maybe other issues in this meta.
Comment #81
bojanz CreditAttribution: bojanz commentedOpened #2399965: Config entity get() and set() should invoke getters/setters, which I feel addresses a major problem with how this was implemented for config entities.
Also, __construct() modifies the properties on its own, without even calling ->set(), so that's a third code path for setting properties, but also the one that's easiest to fix (by making it call ->set()).
Comment #82
filijonka CreditAttribution: filijonka commentedComment #83
Mile23Comment #84
daffie CreditAttribution: daffie commentedAdded two new ones: #2525002: Make the class variables protected for Migration and #2525068: Document the class variable Node::in_preview as will stay public.
Comment #85
daffie CreditAttribution: daffie commentedI would be nice if we could add a test that would test if there are public class variables in subclasses of ContentEntityBase and ConfigEntityBase.
Comment #86
daffie CreditAttribution: daffie commentedAdded #2527076: Make the class variables protected for Drupal\Core\Datetime\Entity\DateFormat to the TODO list.
Comment #87
daffie CreditAttribution: daffie commentedAdded #2527114: Create PHPUnit test that Entity class variables are protected or are allowed public to make sure that there will be no new public class variables added as a accident.
Comment #88
daffie CreditAttribution: daffie commentedComment #89
daffie CreditAttribution: daffie commentedI do not know if we can mark this meta issue as fixed.
All class variables are now protected, but there are some issues open:
Comment #90
andypostRelated also needs to be fixed
Comment #91
catchDowngrading to normal and moving to a plan. I think everything important is tracked in its own issue now or already fixed.
Comment #92
xjmAlso, AFAIK, there aren't any remaining children that are actually RC deadline -- if there are though, we'd put that on the child issue.
Comment #101
andypost