This issue will demonstrate what I proposed in #10 of #1832630: Decide on coding standard for configuration keys of ConfigEntities.

TL;DR:

- We want to keep the snake_case config keys, preserving consistency with plain config files
- We want to have camelCase properties on the class, preserving consistency with other classes in the system, and respecting the coding standards.
We can do this with minimum effort by mapping the snake_case config keys to the camelCase properties.
So I suggest having a defined mapping for each config entity, as a part of the annotation.
The mapping would only be used if found, allowing us to fix config entity types one by one.

Comments

bojanz’s picture

Status: Active » Needs review
StatusFileSize
new6.71 KB

Initial prototype. Fixes NodeType as an example.

diff --git a/core/modules/node/src/Entity/NodeType.php b/core/modules/node/src/Entity/NodeType.php
index 8a08d26..defe45f 100644
--- a/core/modules/node/src/Entity/NodeType.php
+++ b/core/modules/node/src/Entity/NodeType.php
@@ -29,6 +29,16 @@
  *   },
  *   admin_permission = "administer content types",
  *   config_prefix = "type",
+ *   config_mapping = {
+ *     "name" = "name",
+ *     "type" = "type"
+ *     "description" = "description"
+ *     "help" = "help"
+ *     "new_revision" = "newRevision"
+ *     "preview_mode" = "previewMode"
+ *     "display_submitted" = "displaySubmitted"
+ *     "third_party_settings" = "third_party_settings"
+ *   },

third_party_settings need to be changed at all places at once, so I've kept them as it is right now.

tim.plunkett’s picture

  1. +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
    @@ -91,7 +91,20 @@
    +    foreach ($values as $key => $value) {
    ...
    +    foreach ($values as $key => $value) {
    +      $this->$key = $value;
    

    Why not just skip the manipulation of $values?
    Something like this:

    foreach ($values as $key => $value) {
      $mapped_key = isset($config_mapping[$key]) ? $config_mapping[$key] : $key;
      $this->$mapped_key = $value;
    }
    
  2. +++ b/core/modules/node/src/Entity/NodeType.php
    @@ -29,6 +29,16 @@
    + *     "name" = "name",
    + *     "type" = "type"
    + *     "description" = "description"
    + *     "help" = "help"
    

    You could leave these out

Status: Needs review » Needs work

The last submitted patch, 1: 2411967-config-entity-key-mapping-1.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new8.68 KB
new7.5 KB
  1. +++ b/core/modules/node/src/Entity/NodeType.php
    @@ -29,6 +29,16 @@
    + *     "name" = "name",
    + *     "type" = "type"
    + *     "description" = "description"
    + *     "help" = "help"
    + *     "new_revision" = "newRevision"
    + *     "preview_mode" = "previewMode"
    + *     "display_submitted" = "displaySubmitted"
    + *     "third_party_settings" = "third_party_settings"
    

    This failed first because of missing commas here.

  2. +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
    @@ -244,12 +257,25 @@ public static function sort(ConfigEntityInterface $a, ConfigEntityInterface $b)
    +      if (!isset($config_mapping[$name])) {
    +        continue;
    +      }
    

    Then it failed because not every exported value will be in $config_mapping.

I included my suggestions from #3.

I also included unit tests (which required a bit of rejiggering of ConfigEntityBaseUnitTest).

Finally, I removed the dual typing of config_mapping in EntityTypeInterface. We'll be served just as well by an empty array.

-------

One last thought I had, which I didn't touch: this isn't really config_mapping, but config_key_mapping. Thoughts on renaming it?

Status: Needs review » Needs work

The last submitted patch, 4: 2411967-config_key_mapping-4.patch, failed testing.

bojanz’s picture

Big thanks, Tim. Wasn't expecting anyone to see my 3AM experiment before I woke up :)
This now looks much cleaner. I agree that config_key_mapping sounds better, we can make the switch in the next patch.

I'm working on the test fails.

EDIT:
So, we opened pandora's box by using an external service in a constructor. The amount of tests that need fixing is non-trivial.
We need to move the mapping from __construct to avoid it.

bojanz’s picture

Status: Needs work » Needs review
StatusFileSize
new9.35 KB
new4.13 KB

Thanks to alexpott for helping me figure out what was wrong with the tests.

This one should be green. Moved the mapping logic from __construct() to preCreate(), fixed the tests to make sense.
Changing the annotation key is still TODO.

Do we want to handle additional conversions here? ConfigTest, Editor, CommentType should all be as trivial as NodeType.

bojanz’s picture

StatusFileSize
new1.62 KB
new9.5 KB

Reverted a toArray() change from the previous patch that would have caused an issue if the id key is a mapped property.
Killed a kitten by fixing the "$display_submtited" variable name.

The last submitted patch, 7: 2411967-config_key_mapping-7.patch, failed testing.

tim.plunkett’s picture

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
@@ -274,6 +277,21 @@ protected function getTypedConfig() {
+  public static function preCreate(EntityStorageInterface $storage, array &$values) {

Since this isn't implemented on the base classes, none of the entity classes overriding it call parent::, so this won't run. Not many config entities override it, but View certainly does.

Status: Needs review » Needs work

The last submitted patch, 8: 2411967-config_key_mapping-8.patch, failed testing.

bojanz’s picture

Status: Needs work » Needs review
StatusFileSize
new7.38 KB
new16.88 KB

I've confirmed to Tim on IRC that no config entity other than Views implements preCreate(), and the Views one does call the parent.

New patch:
NodeTypeForm was still setting the snake_case keys, and expecting them to be reflected on the entity.
I've introduced a ConfigEntityForm (resolving a @todo in the process) that now does the required key mapping.
Question: What should be done with the now empty EntityForm::copyFormValuesToEntity?

Let's see what the test bot says.

EDIT: I forgot that emptying EntityForm::copyFormValuesToEntity would break unconverted config entity forms. Putting it back as soon as I confirm there are no remaining test fails.

Status: Needs review » Needs work

The last submitted patch, 12: 2411967-config_key_mapping-12.patch, failed testing.

yched’s picture

ConfigEntityForm::copyFormValuesToEntity() : $entity or $this->entity ?

Although : if the snake_case properties are only for config records, while runtime objects have camelCase properties, then the forms should manipulate the camelCase properties ?

I.e you'd have
'#default_value' => $entity->myProperty
$form['myProperty']
$form_state['values']['myProperty']
rather than 'my_property' ?

Meaning, no mapping logic in the form, only on "save to a config record" ?

bojanz’s picture

The entity/$this->entity inconsistency was there in the parent, I just copied it over.

As for camelCase in the form keys, I didn't think I could get away with that. Let's give it a shot.

yched’s picture

More accurately, #2016679: Expand Entity Type interfaces to provide methods, protect the properties is about moving all properties to protected with getters, and the generic get() / set(), so my snippet in #14 would rather be something like :

// on form build :
$form['myProperty'] = array(
  '#default_value' => $entity->get('myProperty'),
);
// on form submit
foreach ($form_values as $property => $value) {
  $entity->set($property, $value);
}

Doesn't change much - either the form uses the camelCase properties and you can do the generic "assign form values into the object" as above, or the form uses snake_case properties and we need to use the mapping.

Yeah, camelCase entries in $form would look a bit uncommon, and not sure what kind of HTML attributes they would produce ('id', 'name'...), maybe that's not ideal.

But with the mapping (current patch) we'd have :
- entity load: map snake to camel (config record to $entity properties)
- form build : map back camel to snake ($entity properties to $form structure) - but that step will more likely be a series of explicit "$form['snake_case'] = array(...$entity->get('camelCase')...)", that needs to comply to the mapping without actually using the mapping.
- form submit : map snake to camel ($form_values to $entity properties)
- entity save : map back camel to snake ($entity properties to config record)
That's a hell of a dance... :-/

It also makes the mapping be not a "config mapping", but a "mapping for places where we'd prefer to have camelCase (object properties) and places where we'd prefer to have snake_case (config records, form structures)".

yched’s picture

On a different topic, it looks weird that mapping is done in toArray() but mapping back is done in preCreate() :
when loading an existing entity from config, preCreate() doesn't run , only __consruct() does, right ?
So I'm not sure how loading could work with the current patch ?

There's currently a (ok, unwritten :-)) contract that toArray() produces values that are valid for passing back to __construct().
That's what config entity loading from CMI records relies on.
That's also the base for the serialization logic implementend in EntityDisplayBase (and that #1977206: Default serialization of ConfigEntities tries to generalize to all config entities) - basically: serialize to toArray(), unserialize by passing those values back into __construct($values_from_to_array).
If toArray() maps camelCase -> snake_case, but mapping back is done in preCreate(), then it looks like this contract is broken ?

More generally: toArray() advertizes itself as a generic "dump to an array" mechanism, that happens to be used, among other things, for config record generation. By baking a "camelCase to snake_case" mapping in there, we confiscate it a bit for CMI business logic.

I guess it's not completely unreasonable to state that "the official array dump shape of a ConfigEntity" is one with camel_case keys. Not sure of the exact consequences outside of CMI, though.

bojanz’s picture

Status: Needs work » Needs review
StatusFileSize
new17.88 KB

Thank you, yched. I've tried to address your feedback in the new patch.

Changes:
- Reverted the #12 changes completely, made the NodeTypeForm keys camelCase where needed.
This looks a bit inconsistent, but conceptually it's cleaner than the automated mapping.
- Renamed the annotation key to "config_key_mapping".
- Made ConfigEntityBase::toArray() return camelCase keys (per #17).
- Moved the mapping logic from preCreate to config storage.
- Fixed test fails.

So, the latest approach means:
- Config entities declare camelCase properties and snake_case config keys.
- Config entity forms use camelCase keys, to match the manipulated property.
- entity_create() expects camelCase keys, and ConfigEntityBase::toArray() returns camelCase keys.
- The config entity storage maps the snake_case keys to camelCase keys (and back) when the config entities are being loaded/saved.

All this trouble so that we can avoid having camelCase config keys :)

Status: Needs review » Needs work

The last submitted patch, 18: 2411967-config_key_mapping-18.patch, failed testing.

anavarre’s picture

Issue tags: +Needs reroll
bojanz’s picture

Let's hold off on the reroll, #2432791: Skip Config::save schema validation of config data for trusted data has a chance of making the main part of this patch unnecessary.

berdir’s picture

neetu morwani’s picture

Status: Needs work » Needs review
StatusFileSize
new14.19 KB

Status: Needs review » Needs work

The last submitted patch, 24: allow_config_entities-2411967-24.patch, failed testing.

googletorp’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new14.11 KB
new1.27 KB

Fix merge error in last patch.

Status: Needs review » Needs work

The last submitted patch, 26: allow_config_entities-2411967-26.patch, failed testing.

bojanz’s picture

The patch needs to be reworked to account for #2432791: Skip Config::save schema validation of config data for trusted data which introduced a config_export key, as documented in https://www.drupal.org/node/2481909.
The problem is that core is only using the config_export key on export, not on import, so we've been unable to implement the functionality in contrib.
So, we need to prove that with a failing test and then take it from there, merging in other parts of the current patch.

LKS90’s picture

StatusFileSize
new1.7 KB
new10.11 KB

Alright, here are two patches: One adds a test to ConfigEntityStorageTest which tests the camelCase to snake_case conversion in core (which is working), the second one does the same for flag, the tests will break because camelCase/snake_case import is not yet supported for contrib modules.

I don't know if the flags tests will even be run/if the patch "applies", but we can do manual testing I guess?

ajits’s picture

Status: Needs work » Needs review

The last submitted patch, 29: core_test_working.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 29: switch_flag_to_camel_case.patch, failed testing.

ajits’s picture

Status: Needs work » Needs review
StatusFileSize
new1.65 KB

Patch was not applying.

arla’s picture

I think that is not the test you are looking for (*jedi hand gesture*). The critical part is that the value is read from protected_property in the DB, and then set to $entity->protected_property but it should be set to $entity->protectedProperty. So something like assertEqual($entity->protectedProperty, ‘TEST’) would make more sense imho.

LKS90’s picture

StatusFileSize
new2.39 KB

Here is a new patch, which fails for the new getProtectedProperty() method. It is NULL instead of TEST. So something is really wrong there.

I left the toArray stuff in (the test method could use a renaming) and added a line which tests the camelCasing for toArray, which we seem to want according to #18:

So, the latest approach means:
- Config entities declare camelCase properties and snake_case config keys.

That seems to work.

- entity_create() expects camelCase keys, and ConfigEntityBase::toArray() returns camelCase keys.

entity_create still uses snake_case, when I switch to camelCase, all three tests fail.

- The config entity storage maps the snake_case keys to camelCase keys (and back) when the config entities are being loaded/saved.

That doesn't seem to work. I added two tests: one which returns the property as defined in code. That one fails, strangely. And another one which returns the property witch snake_casing. That actually works, even though there is no property in the code, the import messes it up.

LKS90’s picture

StatusFileSize
new1.85 KB

Forgot the interdiff, sorry!

Status: Needs review » Needs work

The last submitted patch, 35: allow_config_entities-2411967-35.patch, failed testing.

tim.plunkett’s picture

Version: 8.0.x-dev » 8.1.x-dev

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

jacobsanford’s picture

@LKS90's patch in #35 no longer applied to 8.4.x. A reroll with no further modifications is attached in case this issue still needs to go forward.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

ayush.khare’s picture

StatusFileSize
new2.47 KB
new2.87 KB

Reroll of #42 for 10.1.x

pooja saraah’s picture

StatusFileSize
new2.52 KB
new2.24 KB

Fixed Failed Commands on #54
Attached patch against Drupal 10.1.x
Attached interdiff

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

quietone’s picture

Status: Needs work » Closed (works as designed)

This is a proposal implementing an idea in another issue, #1832630: Decide on coding standard for configuration keys of ConfigEntities. I've just closed that as outdated, so it makes sense to do that to this one as well.