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
Reference: https://www.drupal.org/core/beta-changesIssue 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:
- BC break for anything using the public properties: code will need to convert to the methods
- BC break for anything (mis)using properties that should not really be public: will require minor refactoring
- BC break for alternate implementations of a given entity interface (rare/probably nonexistent): they will need to implement the new methods
|
---|
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.
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.