At #1777956-35: Provide a way to define default values for entity fields raised that having empty field items in $field objects is weird - as others. We've an empty field item object by default such that this gets cloned during prototyping the field object, thus speeds up getting further field objects.

"Sometimes there is an empty FieldItem, sometimes there isn't" sounds wrong ?

I think both cases are semantically equivalent as in both cases the overall field would be empty and storage would remain the same. So whether there is an empty field item or not is not something that matters to the persistent state of the entity, however yes - the different run-time state matters in some scenarios like should an empty item be rendered or presented in a form.

Right now, I fear we need to have that items due to the performance improvements it brings us. But let's research ways to remove that empty item when it's not needed here.

Thinking about it having getPropertyInstance() call setValue(NULL) for a NULL $value also should be good enough and still use the prototyped/cloned field item when a value is given to getPropertyInstance() - but not later on. Performance wise changes later on are not interesting, so that should not be a performance regression (to be verified).

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

fago’s picture

Issue tags: +Entity Field API

tagging

fago’s picture

Status: Active » Needs review
FileSize
727 bytes

I'm talking of that, let's see what the bot says.

fago’s picture

Issue tags: +needs profiling
fago’s picture

FileSize
716 bytes

Fixed/improved comment.

The last submitted patch, d8_no_empty_items.patch, failed testing.

fago’s picture

Status: Needs work » Needs review

#4: d8_no_empty_items.patch queued for re-testing.

Status: Needs review » Needs work
Issue tags: +needs profiling, +Entity Field API

The last submitted patch, d8_no_empty_items.patch, failed testing.

fago’s picture

Issue tags: +Typed sanity

tagging

das-peter’s picture

Assigned: Unassigned » das-peter
das-peter’s picture

Status: Needs work » Needs review
FileSize
1.65 KB
2.35 KB
  • The map type has internally to ensure it deals with a traversable / non-emtpy value before iterating over it.
  • DenormalizeTest::testMarkFieldForDeletion() needs to differentiate between NULL and an empty array instead of NULL and an array with NULL values. Since there were no other test fails we assume the services handling does this already that way.
fago’s picture

+++ b/core/lib/Drupal/Core/TypedData/Type/Map.php
@@ -42,10 +42,12 @@ class Map extends TypedData implements \IteratorAggregate, ComplexDataInterface
+    if (!empty($this->values)) {
+      foreach ($this->values as $name => $value) {
+        $definitions[$name] = array(
+          'type' => 'any',
+        );
+      }

I think we ran over that hunk already in another issue as well.

+++ b/core/modules/hal/lib/Drupal/hal/Tests/DenormalizeTest.php
@@ -108,7 +108,7 @@ public function testMarkFieldForDeletion() {
+    $this->assertTrue(is_null($no_field_value) && empty($empty_field_value) && is_array($empty_field_value), 'A field set to an empty array in the data is structured differently than an empty field.');

We usually use isset() for checking null values.

Else patch looks good to me. So we need to
a) verify it really removes the ugly default values
b) comes not with a performance regression

However, yched pointed out that having those empty items would be a performance regression also once we start invoking field methods on existing items (prepareView, preSave, ...)

yched’s picture

If this removes the empty Item that's present in Field::$list by default, is there maybe some code in some isEmpty() somewhere that had to account for that and can now be simplified ?

fago’s picture

We cannot do that, as this does not remove the possibility of having those empty items. You still can set according empty values which would have to be filtered out be the according filterEmptyItems() method.

das-peter’s picture

I think we ran over that hunk already in another issue as well.

Anything to do about / because of this?

We usually use isset() for checking null values.

Changed is_null() to !isset().

I ran a performance test using the xhprof-kit(using 100 loops)
Results:

ct  : 1,973,449|1,994,736|21,287|1.1%
wt  : -2,147,483,648|-2,147,483,648|0|0.0%
cpu : 12,406,250|12,437,500|31,250|0.3%
mu  : 78,406,432|78,420,512|14,080|0.0%
pmu : 96,046,864|96,059,032|12,168|0.0%

The patched branch had a performance regression of 0.3% compared to the base line. The addition of 1.1% for function calls was expected.

I've tested using 489 comments (to avoid the BC-decorator on nodes) all of them shown on one page in a view formatted as table (because of #2020353: Comments aren't displayed in Views when using Format: Unformatted list and Show: Comment | Full comment)

yched’s picture

@fago: I think I bumped into some code yesterday in TypedData / EntityNG that seemed to be specifically about those "present by default" items. But I can't really remember where, so, well, never mind me :-)

jibran’s picture

Status: Needs review » Needs work
Issue tags: +needs profiling, +Entity Field API, +Typed sanity

The last submitted patch, d8-avoid-empty-field-items-1988492-14.patch, failed testing.

das-peter’s picture

Status: Needs work » Needs review
FileSize
2.4 KB

They see me re-rollin', they lovin' :P

Status: Needs review » Needs work

The last submitted patch, d8-avoid-empty-field-items-1988492-18.patch, failed testing.

das-peter’s picture

Hmm, looks like there was a change I'm not sure how to handle.
While the updated patch should avoid lot's of errors I'm not sure if this is really the right approach.

As of now TypedDataManager::getPropertyInstance() is called by FieldItemBase::__construct() without passing a value. That's a first and it seems like the currently optional value parameter of TypedDataManager::getPropertyInstance() shouldn't be optional.
With this patch NULL is allowed and set as property-value. However, since NULL is the default value of the optional value parameter, there's no guarantee that NULL is really an appropriate property-value.

When e.g. an user object is built FieldItemBase::__construct() calls TypedDataManager::getPropertyInstance() to build the computed property for the user image. Now, since no value is provided, TypedDataManager::getPropertyInstance() set's the property-value to NULL. But later on FileWidget::formElement() expects the property to be an array.
The question now is:

  • Should the value parameter of TypedDataManager::getPropertyInstance() be really optional?
    • Yes: How do we ensure to always set an appropriate value? (Default value method?)
    • No: How do we ensure FieldItemBase::__construct() sets an appropriate value?

The attached patch only changes the behaviour of FileWidget::formElement() to not assume it gets an array as property-value.

fago’s picture

I think it's the job of the caller to set a suiting value. So in case of entities, it's either a new one with default values, or one with loaded values. If both contain an array the field contains always an array and the formatter can rely on it. Still, it's fine to default to have the factory create the object with a NULL value by default imho?

Now, since no value is provided, TypedDataManager::getPropertyInstance() set's the property-value to NULL. But later on FileWidget::formElement() expects the property to be an array.

That sounds like a bug in there somewhere, then? It cannot rely on array() if no one sets that?

das-peter’s picture

Status: Needs work » Needs review

I think it's the job of the caller to set a suiting value.

Agreed.

...Still, it's fine to default to have the factory create the object with a NULL value by default imho?...
...That sounds like a bug in there somewhere, then? It cannot rely on array() if no one sets that?...

That's where I'm not sure about. Currently I assume that if TypedDataManager::getPropertyInstance() wouldn't be called by FieldItemBase::__construct() the assumption of having an array as value is valid (Even though a bit bold).
Thus the patched version of TypedDataManager::getPropertyInstance() could be held responsible for breaking this assumption.

However, let's see what the test-bot says to the patch in #20

Status: Needs review » Needs work

The last submitted patch, d8-avoid-empty-field-items-1988492-20.patch, failed testing.

das-peter’s picture

Status: Needs work » Needs review
FileSize
2.04 KB
3.06 KB

Hmm, looks like I was looking at the wrong place to figure out why the empty array is lost.
Not FieldItemBase::__construct() but ItemList::createItem() should ensure it doesn't set NULL when an empty array would be more appropriate.
FieldAttachStorageTest has tests like this $this->assertIdentical($read->{$this->field_name}[0]->getValue(), array(), "The test entity revision $vid is deleted."); and for me that looks absolutely valid and we should try to meet these "assumptions".

yched’s picture

#24 looks better. Callers of getValue() should not have to worry about "should I check that it's a real array". See http://www.garfieldtech.com/blog/empty-return-values

das-peter’s picture

Callers of getValue() should not have to worry about "should I check that it's a real array"

I totally agree. I just wasn't sure where I've to / can fix that. And frankly speaking I'm still not sure it's fixed in the right place now.

Status: Needs review » Needs work

The last submitted patch, d8-avoid-empty-field-items-1988492-24.patch, failed testing.

yched’s picture

We really need to fix that IMO. In various patches I hit bugs with $items that are "in theory empty but in reality contain an $item object with NULL properties", which break various stuff in various subtle ways. Those require adding safeguard ->filterEmptyValues() calls in more and more places. This looks messy, and it's fairly hard to predict in which part of the code flow an $items object can be considered safe or not.

Berdir’s picture

Status: Needs work » Needs review
Issue tags: -needs profiling, -Entity Field API, -Typed sanity

Status: Needs review » Needs work

The last submitted patch, d8-avoid-empty-field-items-1988492-24.patch, failed testing.

Anonymous’s picture

Issue summary: View changes

Updated issue summary.

yched’s picture

Issue summary: View changes
Status: Needs work » Needs review
FileSize
643 bytes

We still very much need to get rid of that issue.

Many things might have changed meanwhile, so rebooting the issue with a silly patch that just removes the "$this->list[0] = new Item" in FieldItemList::__construct().

From manual testing, basic stuff like installing drupal, creating and viewing a node, still work. Let's see what breaks.

yched’s picture

Component: typed data system » field system
Assigned: das-peter » Unassigned
Category: Task » Bug report
Priority: Normal » Major

This is a bug and is at least major, IMO.

Berdir’s picture

The last submitted patch, 33: 1988492_empty_item_list-32-remove-filter.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 33: 1988492_empty_item_list-32.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
FileSize
4.68 KB
707 bytes

Re-adding the filter call before saving, this will hopefully fix most of them and is fine to keep.

Status: Needs review » Needs work

The last submitted patch, 36: 1988492_empty_item_list-36.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
FileSize
5.58 KB
926 bytes

ImageItem shouldn't blindly assume that $this->entity can be loaded.

Status: Needs review » Needs work

The last submitted patch, 38: 1988492_empty_item_list-38.patch, failed testing.

yched’s picture

Status: Needs work » Closed (duplicate)