Problem/Motivation
Quoting
#2016679: Expand Entity Type interfaces to provide methods, protect the properties β
:
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.)
The result of that issue was:
1) Content entities now have getters and setters which act as wrappers around the get() and set() methods.
2) Config entities now have getters and setters, and don't care about get() and set() at all.
This means that unlike content entities, config entities now have two ways (and code paths) to get or modify a property.
This makes all benefits of the getters and setters moot, since they can be bypassed (and actually are, by CMI, forms, entity_create(), etc).
EDIT: There's also a third way, the constructor, being fixed in
#2399999: ConfigEntityBase::__construct() should use set() to populate the values β
.
For getters, the main benefit is ensuring BC:
- A config entity has first_name and last_name properties, which get deprecated (removed from the forms, kept in schema) in favor of a more generic name property.
The getName() accessor can see if first_name and last_name are available, and use them to construct and return the name. But ->get('name') will bypass that.
For setters, the benefit is data consistency:
Imagine a workflow entity type that has an array of states:
protected $states = array();
public function setStates(array $states) {
$this->states = $states;
return $this;
}
This ensures getStates() always returns an array, and the data is always valid. But set('states', 'banana') still works. Sure, it will fail on save when the schema is consulted, but other code has plenty of time to crash until then.
Solution
Change ConfigEntityBase's get() and set() to invoke the getters and setters when found.
The property name is camelized, the method's existence is checked (for first_name we search for getFirstName() and setFirstName()), if found it is used.
Otherwise the old behavior is kept (the property is modified directly).