Field API instance CMI data include an entry for "default value".
This currently includes a 'target_id' as a numeric ID, which is totally deployment-unfriendly.

After #2056405: Let field types provide their support for "default values" and associated input UI and #2050801: Unify handling of default values between base and configurable fields field types can now massage default values before saving to & after loading from CMI, by overriding ConfigField::defaultValuesFormSubmit() & ConfigField::getDefaultValue().

entity_ref and taxo field types should use these to switch in UUIDs in a target_uuid entry, instead of numeric ids.
(filing this for entity_ref component, but both modules can probably be dealt with in the same patch)

CommentFileSizeAuthor
#54 default_values_uuids-2090541-54.patch18.97 KBplopesc
#54 interdiff.txt1.5 KBplopesc
#52 default_values_uuids-2090541-52.patch18.81 KBplopesc
#52 interdiff.txt3.44 KBplopesc
#50 default_values_uuids-2090541-50.patch18.94 KBplopesc
#50 interdiff.txt5.53 KBplopesc
#47 default_values_uuids-2090541-47-test-only.patch8.99 KBplopesc
#47 default_values_uuids-2090541-47.patch18.66 KBplopesc
#46 default_values_uuids-2090541-46-test-only.patch8.99 KBplopesc
#46 default_values_uuids-2090541-46.patch17.76 KBplopesc
#41 default_values_uuids-2090541-41-test-only.patch9.13 KBplopesc
#41 default_values_uuids-2090541-41.patch17.49 KBplopesc
#41 interdiff.txt5.55 KBplopesc
#38 default_values_uuids-2090541-38.patch12.61 KBplopesc
#38 interdiff.txt4.35 KBplopesc
#36 default_values_uuids-2090541-35.patch10.37 KBplopesc
#36 interdiff.txt1.34 KBplopesc
#33 default_values_uuids-2090541-33.patch10.33 KBplopesc
#33 interdiff.txt4.16 KBplopesc
#30 default_values_uuids-2090541-30.patch8.4 KBplopesc
#30 interdiff.txt963 bytesplopesc
#27 default_values_uuids-2090541-27.patch8.38 KBplopesc
#27 interdiff.txt8.2 KBplopesc
#25 default_values_uuids-2090541-25.patch8.73 KBplopesc
#25 interdiff.txt2.64 KBplopesc
#20 default_values_uuids-2090541-20-only-tests.patch4.32 KBplopesc
#20 default_values_uuids-2090541-20.patch7.88 KBplopesc
#18 default_values_uuids-2090541-17-only-test-do-not-test.patch4.22 KBplopesc
#18 default_values_uuids-2090541-17.patch7.77 KBplopesc
#13 default_values_uuids-2090541-23.patch3.55 KBplopesc
#13 interdiff.txt2.68 KBplopesc
#9 default_values_uuids-2090541-9.patch3.62 KBplopesc
#9 interdiff.txt3.75 KBplopesc
#6 default_values_uuids-2090541-6.patch3.46 KBplopesc
#6 interdiff.txt3.9 KBplopesc
#3 default_values_uuids-2090541-3.patch3.2 KBplopesc
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

larowlan’s picture

I wonder about {comment}.nid and uid cols in eg node data

yched’s picture

@larowlan : that's nothing that gets exported in CMI files ?

plopesc’s picture

Status: Active » Needs review
FileSize
3.2 KB

Hello

Attaching a first approach for this issue. I found that there are entities that does not have uuid, like users 0 and 1 (Anonymous and admin). Then this patch check it and stores default values in the old w if the entity does not have uuid.

Suggestions are welcomed!

Regards

yched’s picture

Status: Needs review » Needs work

Thanks @plopesc! That's totally the idea.
Review below.

  1. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,64 @@
    +
    +
    

    one empty line too many

  2. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,64 @@
    +class ConfigurableEntityReferenceField extends ConfigField {
    

    Missing a phpdoc

  3. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,64 @@
    +    $stored_value = $this->getFieldDefinition()->getFieldDefaultValue($this->getEntity());
    +    // If retrieved value is NULL, exit.
    +    if (!isset($stored_value)) {
    +      return $stored_value;
    +    }
    +
    +    // Process values, converting uuids on their corresponding target_id.
    +    $default_value = array();
    

    I think we should start with $default_value = parent::getDefaultValue(), then iterate and replace uuids if we find some.

    Also note: core standardizes on capital letters in comments : UUID, UUIDs (comments only, not property names or variable names)

  4. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,64 @@
    +      if (isset($item['uuid'])) {
    

    since the original property is 'target_id', I'd vote for 'target_uuid' ?

  5. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,64 @@
    +        $entity = entity_load_by_uuid($target_type, $item['uuid']);
    

    is there a way to do a multiple-load ? it *seems* StorageController::loadByProperties(array('uuid' => array(several uuids)); will load all the entities in one single multiple-load.

  6. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,64 @@
    +        $default_value[] = array('target_id' => $entity->id(), 'revision_id' => $entity->getRevisionId());
    

    'revision_id' is more complex, we cannot just ditch it on submit and use the "current revision" of the entity associated to the uuid on load.

    However, revisions don't have UUIDs, so I'm not sure how we can support referencing a specific revision in entity_reference field...

  7. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,64 @@
    +    $default_value = array();
    +    $target_type = $this->getFieldDefinition()->getFieldSetting('target_type');
    +    // Extract the submitted value.
    +    $widget = $this->defaultValueWidget($form_state);
    +    $widget->extractFormValues($this, $element, $form_state);
    +    $value = $this->getValue();
    

    Similarly, this should be parent::defaultValuesFormSubmit()

  8. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,64 @@
    +    foreach ($value as $index => $item) {
    +      $entity = entity_load($target_type, $item['target_id']);
    

    This should use entity_load_multiple(), though - and preferably, \Drupal->entityManager()->getStorageController($target_type)->loadMultiple();
    First, iterate on values to collect ids, then load the entities, then iterate again to assign uuids from the loaded entities.

  9. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,64 @@
    +  }
    +}
    

    Minor: missing empty line after the last method of a class

  10. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,64 @@
    \ No newline at end of file
    

    should not happen :-)

amateescu’s picture

--- /dev/null
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php

Apart form @yched's excellent review, this file path bugs me a little. I don't think this should live under the field_type plugin namespace. I know FileField currently does, but I don't like it there either :)

plopesc’s picture

Status: Needs work » Needs review
FileSize
3.9 KB
3.46 KB

Hello.
Attaching patch that addresses all points in #4 except 5 and 6, I think. Here my comments about thse points:
5.- Multiple load in getDefaultValue() makes code a bit weird IMHO. Maybe it loses consistency because it depends on loadByProperties() does not change the entities order.

6.- In cases of revisions, we could kept the target_id/revision_id pattern, in order to maintain the reference to a specific revision. It is the same approach we are following for non UUID entities.

I think we could split the code in getDefaultValue() and defaultValuesFormSubmit() to try to keep it cleaner, what do you think?

About #5: @amateescu, I'm following the same approach that FileField. Mabe we could open a follow up issue to decide where put these classes. Or if you suggest a a better place, I have no problem to move there ConfigurableEntityReferenceField :)

Regards.

yched’s picture

Thanks for being so quick ;-)

  1. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,75 @@
    +    foreach ($default_value as $index => $item) {
    

    Use $delta rather than $index, for consistency with the rest of core.
    $item might be misleading, since, *unlike* everywhere else in core, this is an array and not a Field (list of FieldItem's) object.
    Maybe $properties ?

  2. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,75 @@
    +        $uuids[] = $item['target_uuid'];
    +        $indexes[] = $index;
    

    If you key $uuids by delta, you shouldn't need the separate $indexes array.

  3. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,75 @@
    +    if (empty($uuids)) {
    +      return $default_value;
    

    Matter of taste, but I'd rather wrap the rest of the code in if ($uuids) { rather than exit early. Exit at the end, less code paths to figure out mentally.

  4. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,75 @@
    +    foreach ($indexes as $index => $item) {
    +      $entity = current($entities);
    

    I don't think we can rely on the order of the $entities array returned by loadByProperties(), the current implementation might preserve the order, but it's not documented anywhere and thus could change at any given time.

    So IMO we need to iterate on entities first, to build an $ids array of (uuid => id). Then,

    foreach ($ûuids as $delta => $uuid) {
     $default_value[$delta] = array('target_id' => $ids[$uuid], ...)
    }
    
  5. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,75 @@
    +    foreach ($default_value as $index => $item) {
    

    same as above, $delta => $properties

  6. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,75 @@
    +      $items[] = $item['target_id'];
    

    misleading var name - $ids instead of $items

  7. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,75 @@
    +      $entity = $entities[$item['target_id']];
    +      $uuid = $entity->uuid();
    

    Minor, but the intermediate $entity variable could go away, $uuid = $entities[$item['target_id']]->uuid() is fine.
    Also, the if(isset($uuid)) could use a comment about some entities not having UUIDs (and why it's not an issue - if that's not an issue ?)

yched’s picture

Multiple load in getDefaultValue() makes code a bit weird IMHO

We can streamline it a bit (see remarks above) - and multiple loading is fairly important for performance, not doing it when we can could qualify as a bug.

I think we could split the code in getDefaultValue() and defaultValuesFormSubmit() to try to keep it cleaner, what do you think?

Let's see how the final code looks like, but I don't think that should be needed. Let's avoid cluttering the class with a separate helper method unless we need to.

plopesc’s picture

Issue tags: +Field API
FileSize
3.75 KB
3.62 KB

Addressing suggestions in #7.

We can streamline it a bit (see remarks above) - and multiple loading is fairly important for performance, not doing it when we can could qualify as a bug.

Yeah, I know :) I was talking about the code readability. I think it is now better after changes suggested in #7

Now, we should decide how to manage the revision_id issue (I suggested a possible approach in #6) and the best place for the ConfigurableEntityReferenceField class.

Regards.

Status: Needs review » Needs work
Issue tags: -Field API

The last submitted patch, default_values_uuids-2090541-9.patch, failed testing.

plopesc’s picture

Status: Needs work » Needs review
Issue tags: +Field API
yched’s picture

  1. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,76 @@
    +    // If retrieved default_value is not NULL, we can massage it.
    +    if ($default_value) {
    +      // Process values, converting UUIDs on their corresponding target_id.
    

    First comment is not really needed, The second one could be moved above the if(). Also, "process" is vague &nd doesn't add much. Just "Convert UUIDs to numeric IDs." ?

    Leave one empty line after the parent call, like you do in the other method ? (consistency ++)

  2. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,76 @@
    +    // Process values and store depending on UUID existence.
    

    "Convert numeric IDs to UUIDs to ensure config deployability" ?

  3. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,76 @@
    +      // Some entities does not have uuid. It's not an issue?
    +      // If it's not an issue, why?
    

    This should be left as a @todo then :-).
    And we need to solve that. What were those cases of entities not having a UUID ?
    + language : s/does/do

  4. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/field/field_type/ConfigurableEntityReferenceField.php
    @@ -0,0 +1,76 @@
    +        $default_value[$delta] = array('target_uuid' => $uuid);
    

    Actually I don't think we should overwrite the value completely, just set 'target_uuid' and unset 'target_id', leaving the rest untouched, whatever that may be now or in the future - and same in getDefaultValue(). The job here is UUID <-> ID, nothing more.

Regarding revision_id, I think the current UI doesn't let you select a specific revision anyway, so the revision_id entry will be empty. Let's just not care about that. How e_r can reference a specific revision is not fully clear to me, and this can be adressed in a followup.

Regarding namespace / folder for the Field class, I do think it's generally preferable to keep Field classes next to the FieldItem classes, both are really related. Putting non plugin classes in plugin discovery folders is fine, that's what we do with base classes for widgets and formatters. Sensible code organization beats minor overhead on plugin cache miss IMO.

plopesc’s picture

Sorry for the grammar errors... ;)

Re-rolling fixing comments and changing only UUID <->ID instead of the whole default_value array.

Regards.

yched’s picture

So, regarding users 0 and 1:
I opened #2092603: Users 0 and 1 have no UUID about the missing UUIDs. Meanwhile, we could special case here and use 'anonymous' resp. 'administrator' as 'target_uuid' strings for user 0 resp. 1 ? (only if target_type is user of course) - with a @todo to clean this up after that other issue is fixed. Will make the methods a bit more convoluted, but that should be temporary...

I'm not actually sure entity_ref allows referencing the anonymous user, BTW. @amateescu ?

Also, we should add tests:
- Create an entity_ref field / instance through Field UI, enter a default value in the form (necessarily with a numeric id), check that the CMI record contains a UUID
- field_info_cache_clear()
- Create a fresh entity (with entity_create()), and check that it correctly receives the default value (field has the expected numeric target_id)

amateescu’s picture

I'm not actually sure entity_ref allows referencing the anonymous user, BTW.

Yes, it does :)

And you're right that we don't need to worry about revision_id here, the support for it exists only in the schema and some internal code, but it's not exposed in the UI at all. It will definitely need more attention at some point.

yched’s picture

@amateescu: ok - it's just that HEAD gives me an "Integrity constraint violation: 1062 Duplicate entry '' for key 'name': INSERT INTO {users}" fatal when I try to create a node referencing user 0 (using a select widget) :-). Is it a known bug or should I open an issue for that ?

Berdir’s picture

My guess is that Users need a different isNew() implementation, that returns FALSE when uid === 0.

plopesc’s picture

Hello

I've been working in tests, but I'm not able to get it working. When I try to execute field_info_cache_clear(), it throws the folloeing exception: Drupal\Core\Entity\EntityStorageException: SQLSTATE[HY000]: General error: 1366 Incorrect integer value: '' for column 'field_entity_reference_revision_id' at row 1

However, if I skip the cache clear, when I create the new fresh entity, the entity_reference field default value is not set.

I reproduced the test steps manually and it works fine. I also tried to pass the same tests against 8.x branch and experienced the same annoying issue. (To test against 8.x branch, you should comment lines 87 - 89 regarding to target_uuid)

Any clue? I'm noob in D8 tests... and code is not already polished, just proof of concept.

Regards

Status: Needs review » Needs work

The last submitted patch, default_values_uuids-2090541-17.patch, failed testing.

plopesc’s picture

Status: Needs work » Needs review
FileSize
7.88 KB
4.32 KB

Hello

After digging a bit more, I found that entities created through entity_create() does not include field default values... I've checked it with taests against entity_reference and text fields.

Is this right? Is this a known bug?

Creating the new node through the UI, default values are set correctly and tests works now :)

Regards.

Status: Needs review » Needs work

The last submitted patch, default_values_uuids-2090541-20.patch, failed testing.

yched’s picture

entity_create() should absolutely initialize fields with their default values - and according to a quick test, this does work in today's HEAD.

larowlan’s picture

I've done a lot of digging into how the applyDefaultValues stuff works over in #731724: Convert comment settings into a field to make them work with CMI and non-node entities, and if there is a default value in the instance settings, the applyDefaultValue function isn't called. Not sure if that helps.

yched’s picture

Well, on current HEAD:
- in Field UI, edit 'body' field instance on article nodes, and add a default value there
- then entity_create('node', array('type' => 'article'))->get('body')->getValue(); does return the default values I defined, meaning they get correctly assigned on a fresh entity.

plopesc’s picture

Status: Needs work » Needs review
FileSize
2.64 KB
8.73 KB

New tests trying to demonstrate that it fails for programmatically created entities.

Some checks let me to think that this could be an issue related to testing environment.

Regards

Status: Needs review » Needs work

The last submitted patch, default_values_uuids-2090541-25.patch, failed testing.

plopesc’s picture

Status: Needs work » Needs review
FileSize
8.2 KB
8.38 KB

Hello
After talking with @yched yesterday, submitting new patch cleaned and tests included. Also included temporary support for users 0 and 1 as suggested in #14 until #2050843: Users 0 and 1 are created without a UUID will have been fixed.

Now, I have a question... Should be necessary a hook_update_N implementation to convert previously values stored and target_id into target_uuid?

Regards.

yched’s picture

No need for an upgrade path for entity_reference fields, since ER is a D8 addition.
But taxonomy_term_reference fields will need an upgrade function, yes.

plopesc’s picture

OK, I don't know if it could be necessary for sites which upgrade from old D8 releass like 8.x-alpha1 to the current version.

About the taxonomy_term_reference fields, should we wait until #2015687: Convert field type to FieldType plugin for taxonomy module will be fixed?

Currently taxonomy_term_reference still defined through hook_field_info() instead of using plugins. We could postpone this until the taxonomy_term_reference will be converted or cover only entity_reference field in this issue and open another once #2015687: Convert field type to FieldType plugin for taxonomy module will be fixed.

What do you think?

Regards.

plopesc’s picture

amateescu’s picture

Status: Needs review » Postponed

I think it's ok to wait for #2015687: Convert field type to FieldType plugin for taxonomy module . Also, ConfigurableEntityReferenceField needs to be renamed to ConfigurableEntityReferenceFieldItemList now ;)

yched’s picture

Status: Postponed » Needs work

At this point in the D8 cycle, we don't provide upgrades for older D8 versions, only for D7

I don't think I see how #2015687: Convert field type to FieldType plugin for taxonomy module is related, or why it should postpone this issue ? Our case here is only about updating the content of field instance definitions that come from D7, and #2015687: Convert field type to FieldType plugin for taxonomy module will not change anything in this area.

taxonomy needs to provide a new update function, and make sure that it runs after field_update_8003() (using hook_update_dependencies() - look at how file_update_dependencies() does it)

The code should be something like this:
(@todo in the middle: write an SQL query to fetch the UUID of a given term_id)

foreach (config_get_storage_names_with_prefix('field.field.') as $config_name) {
  $field_config = \Drupal::config($config_name);
  if ($field_config->get('type') == 'taxonomy_term_reference') {
    foreach (config_get_storage_names_with_prefix('field.instance.' . $field_config->get('entity_type')) as $instance_config_name) {
      $parts = explode('.', $config_name);
      $field_name = end($parts);
      if ($field_name == $field_config->get('name')) {
        $instance_config = \Drupal::config($instance_config_name);
        $default_value = $instance_config->get('default_value');
        foreach ($default_value as $delta => &$value) {
          if (isset($value['tid']) {
            // @todo find the corresponding $uuid
            $value['uuid'] = $uuid
            unset($value['tid'];
          }
        }
        $instance_config->set('default_value', $default_value);
      }
    }
  }
}

[edit: simplified the code a bit]

(Note that taxonomy.module did not provide an update to rename 'tid' to target_id' in default values in $instance config so far, so the D7 upgrade was broken on that regard so far)

plopesc’s picture

Status: Needs work » Needs review
FileSize
4.16 KB
10.33 KB

Hello

Added taxonomy support for default_value target_uuid and hook_update_N implementation that converts tids into target_uuid (as commented in #29 tid to target_id conversion was not done yet).

I've done some manual testing and it worked fine without any problem. However, my local testbot says "Fatal error: Class 'Drupal\entity_reference\Plugin\field\field_type\ConfigurableEntityReferenceFieldItemList' not found in /xxxx/yyyy/zzzz/drupal8/core/lib/Drupal/Core/TypedData/TypedDataManager.php on line 92"

Maybe something related to TypedDataConversion not yet ready?

Regards.

Status: Needs review » Needs work

The last submitted patch, default_values_uuids-2090541-33.patch, failed testing.

yched’s picture

Going afk for the next two weeks, I'll let you guys drive this home :-)

plopesc’s picture

Status: Needs work » Needs review
FileSize
1.34 KB
10.37 KB

After some sleep, I found that the problem was that I used the same ConfigurableEntityReferenceFieldItemList as list_class for taxonomy_term_reference field. Then test fails because it creates a (maybe non desired) dependency on Entity Reference module.

So, we can:

  • Copy & paste the same class in taxonomy module
  • Mark entity reference as dependency of taxonomy
  • Move ConfigurableEntityReferenceFieldItemList to other place in core where both modules can access to it

Any thought?

New patch fixing bug in upgrade function.

Regards.

amateescu’s picture

Component: entity_reference.module » field system
Status: Needs review » Needs work

As sad as it may sound, I think we should go with copying the code for now and let #1847596: Remove Taxonomy term reference field in favor of Entity reference handle the unification.

Also, we need an upgrade path test for the taxonomy ref field.

plopesc’s picture

Status: Needs work » Needs review
FileSize
4.35 KB
12.61 KB

After more digging and testing, found that #2015687: Convert field type to FieldType plugin for taxonomy module is related to some test fails, given that taxonomy field works with LegacyConfigFieldItemList instead of ConfigFieldItemList as default list_class.

So, I created a new class in Taxonomy module named TaxonomyTermReferenceFieldItemList that extends from LegacyConfigFieldItemList where default values are managed.

About the upgrade path test, which steps would check this test? Only the tid -> target_uuid conversion in default value?
If more steps should be tested, maybe would be better open a new follow-up issue and do it.

Regards

Status: Needs review » Needs work

The last submitted patch, default_values_uuids-2090541-38.patch, failed testing.

amateescu’s picture

About the upgrade path test, which steps would check this test? Only the tid -> target_uuid conversion in default value?

Yep, that should do it :)

plopesc’s picture

Status: Needs work » Needs review
FileSize
5.55 KB
17.49 KB
9.13 KB

Hello

New patch including upgrade path test for taxonomy_term default_value.

Status: Needs review » Needs work
Issue tags: -Field API

The last submitted patch, default_values_uuids-2090541-41.patch, failed testing.

plopesc’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, default_values_uuids-2090541-41.patch, failed testing.

plopesc’s picture

Status: Needs work » Needs review
Issue tags: +Field API
plopesc’s picture

Rerolled after #2050835: Move Widget, Formatter, and FieldType plugin types to the Core\Field system is in.

+++ b/core/modules/taxonomy/taxonomy.module
@@ -907,6 +907,13 @@ function taxonomy_field_info() {
 /**
+ * Implements hook_field_info_alter().
+ */
+function taxonomy_field_info_alter(&$info) {
+  $info['taxonomy_term_reference']['list_class'] = 'Drupal\taxonomy\Plugin\field\field_type\TaxonomyTermReferenceFieldItemList';
+}

I don't know why, but 'list_class' parameter is not taken into account if it is declared in hook_field_info(), however, if it's declared in hook_field_info_alter() it works like a charm. Any suggestion?

plopesc’s picture

Rerolled after #2115145: Move field type plugins to Plugin/Field/FieldType.

I found the problem described in #46 about hook_field_info() and 'list_class'. This parameter was overriden in LegacyFieldDiscoveryDecorator::getDefinitions(), so only is possible override it in hook_field_info_alter().

+++ b/core/lib/Drupal/Core/Field/LegacyFieldTypeDiscoveryDecorator.php
@@ -63,10 +63,13 @@ public function getDefinitions() {
-          $definition['id'] = $plugin_id;
-          $definition['provider'] = $module;
-          $definition['configurable'] = TRUE;
-          $definition['list_class'] = '\Drupal\Core\Field\Plugin\Field\FieldType\LegacyConfigFieldItemList';
+          $plugin_defaults = array(
+            'id' => $plugin_id,
+            'provider' => $module,
+            'configurable' => TRUE,
+            'list_class' => '\Drupal\Core\Field\Plugin\Field\FieldType\LegacyConfigFieldItemList',
+          );
+          $definition += $plugin_defaults;

I added this fix in this patch. If you consider it, we could open a follow-up issue where discuss about this.

Regards

yched’s picture

Yay, the tests are awesome !
Minor remarks below.

  1. +++ b/core/lib/Drupal/Core/Field/LegacyFieldTypeDiscoveryDecorator.php
    @@ -63,10 +63,13 @@ public function getDefinitions() {
    -          $definition['id'] = $plugin_id;
    -          $definition['provider'] = $module;
    -          $definition['configurable'] = TRUE;
    -          $definition['list_class'] = '\Drupal\Core\Field\Plugin\Field\FieldType\LegacyConfigFieldItemList';
    +          $plugin_defaults = array(
    +            'id' => $plugin_id,
    +            'provider' => $module,
    +            'configurable' => TRUE,
    +            'list_class' => '\Drupal\Core\Field\Plugin\Field\FieldType\LegacyConfigFieldItemList',
    +          );
    +          $definition += $plugin_defaults;
    

    Agreed with the workaround for 'list_class', but we should keep the other properties forced - just $definition += array('list_class' => ...);

  2. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Field/FieldType/ConfigurableEntityReferenceFieldItemList.php
    @@ -0,0 +1,87 @@
    +      // @todo Some entities do not have uuid. IE: Anonymous and admin user.
    +      //   Remove this exception code once #2050843 has been fixed.
    

    Minor: we usually put full issues URLs rather that #1234 markers in comments. Also, this is not an exception (PHP sense) - "Remove this special case ..." ?

  3. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldDefaultValueTest.php
    @@ -0,0 +1,92 @@
    +  function testEntityReferenceDefaultValue() {
    

    Missies a phpdoc

  4. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldDefaultValueTest.php
    @@ -0,0 +1,92 @@
    +    // Create some content to be referenced.
    ...
    +    // Set created content as default_value.
    

    Minor: in code comments, refer to a node as "node" rather than as "content" (when it's a node...) - clearer :-)

yched’s picture

Also:

+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Field/FieldType/ConfigurableEntityReferenceFieldItemList.php
@@ -0,0 +1,87 @@
+        foreach ($uuids as $delta => $uuid) {
+          $default_value[$delta]['target_id'] = $entity_ids[$uuid];
+          unset($default_value[$delta]['target_uuid']);
+        }

We cannot exclude the fact that we might receive 'target_uuids' that do not exist on the site - $entity_ids[$uuid] won't be set.
In that case, we should unset($default_value[$delta]) - and re-number $default_value at the end to make sure we leave no wholes in deltas (just return array_values($default_value))

plopesc’s picture

Re-rolled including suggestions in #48and #49. Also implemented changes suggested in #49 in TaxonomyTermReferenceFieldItemList.

Regards.

yched’s picture

  1. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Field/FieldType/ConfigurableEntityReferenceFieldItemList.php
    @@ -0,0 +1,100 @@
    +      // Converting UUIDs to numeric IDs.
    

    Minor, phrasing: "Convert" rather than "Converting" ?

  2. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/Field/FieldType/ConfigurableEntityReferenceFieldItemList.php
    @@ -0,0 +1,100 @@
    +      if (!empty($default_value)) {
    +        $default_value = array_values($default_value);
    +      }
    +      else {
    +        $default_value = NULL;
    +      }
    

    Do we really need the empty / !empty cases ?
    Just something like
    // Ensure we return sequential deltas, in case we had to remove unknown UUIDs.
    $default_values = array_values($default_value);
    should work ?

  3. +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceFieldDefaultValueTest.php
    @@ -0,0 +1,96 @@
    +   * Tests that entity_reference fields default values are stored in the
    +   * config entity.
    

    We try to keep those < 80 chars when possible.
    Since we are in a class that tests entity_reference, maybe we can avoid repeating that here ? Something like: "Tests that default values are correctly translated to UUIDs in config." ?

After that, we should really be there ;)

plopesc’s picture

@yched: About comment 2 in #51. I did it because in case all the uuids are not available, it would return array();. However, when there are not default values, it return NULL;.

I made some tests and it looks like there is no difference between NULL and array() for empty default value...

Regards.

yched’s picture

Status: Needs review » Reviewed & tested by the community

Yes, returning an empty array is just fine, that's what happens for regular field types on a field instance where no default value is configured (the CMI file contains "default_value: empty array").

I'd just vote to add the comment suggested in #51 above the array_values() line, because it's not frankly intuitive otherwise.
(// Ensure we return sequential deltas, in case we had to remove unknown UUIDs.)

Minor, other than that, RTBC; Thanks !

plopesc’s picture

I used the following coment line to ensure we keep it < 80 chars:
"// Ensure we return consecutive deltas, in case we removed unknown UUIDs."

Regards.

plopesc’s picture

Issue summary: View changes

foobar

catch’s picture

I keep looking at this and thinking it'd be simpler to always store uuids for entity references, or both uuid and numeric ID, rather than doing the conversion. Is there an issue for that?

This isn't the place to change that so the conversion seems like the only option.

yched’s picture

AFAIK, storing UUIDs instead of numeric local IDs for entity ref field values has been discussed long ago in the entity_reference contrib queue, and dismissed as impacting perf (JOINs if joining on strings rather than ints ?)

Storing / accepting both on input: dunno is this has been discussed, but not sure it would really be simpler.

catch’s picture

Issue summary: View changes
Status: Reviewed & tested by the community » Needs review

Strings on joins is only an issue if you're joining. While sometimes entity reference fields are used in WHERE queries (i.e. to find backreferences), often you're just loading the field values with the entity the field is attached to and the uuid/id isn't going to matter there.

Seems worth discussing. I'm not sure if here or a follow-up - if you strongly think it should be a follow-up let's open it and stick this back to RTBC.

amateescu’s picture

It was also discussed a bit in the core queue, in the first few comments of #1757452: Support config entities in entity reference fields (that issue had a different title initially), and @fago seemed to be against it.

Storing / accepting both on input: dunno is this has been discussed, but not sure it would really be simpler.

It would make *this* patch simpler, but it would complicate the code in ER itself, not really sure how much, so I can't say I'm against it.. it's worth a try.

Strings on joins is only an issue if you're joining. While sometimes entity reference fields are used in WHERE queries (i.e. to find backreferences), often you're just loading the field values with the entity the field is attached to and the uuid/id isn't going to matter there.

We should also keep in mind that even without joins, when you want to load a bunch of entities by uuid, the system does an additional entity query to figure out the main ids. This wouldn't matter if we'd store both.

yched’s picture

Status: Needs review » Reviewed & tested by the community

JOINs are mostly leveraged by views relationships. But WHEREs would be impacted too, and they appear a lot with filters / contextual filters.

But yes, I think storing UUIDs is a whole discussion in itself. If/when this happens, the code added here will be easy to adapt/revert. Meanwhile, let's do this, coz config deployability is screwed without it.

yched’s picture

catch’s picture

Status: Reviewed & tested by the community » Fixed

OK I think only both is going to be viable based on the past comments, and that's more or less denormalizing at that point which definitely needs its own discussion. Committed/pushed to 8x., thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

plopesc’s picture