Problem/Motivation

Here's what Drupal\Core\Entity\ContentEntityBase::set() looks like:

  /**
   * {@inheritdoc}
   */
  public function set($name, $value, $notify = TRUE) {
    // If default language or an entity key changes we need to react to that.
    $notify = $name == 'langcode' || in_array($name, $this->getEntityType()->getKeys());
    $this->get($name)->setValue($value, $notify);
  }

Note the {@inheritdoc}. That comes from Drupal\Core\Entity\FieldableEntityInterface, which says this:

   * @param string $field_name
   *   The name of the field to set; e.g., 'title' or 'name'.
   * @param mixed $value
   *   The value to set, or NULL to unset the field.
   * @param bool $notify
   *   (optional) Whether to notify the entity of the change. Defaults to
   *   TRUE. If the update stems from the entity, set it to FALSE to avoid
   *   being notified again.

The relevant part being $nofity, which is supposed to allow you to change the notification behavior.

I'm not entirely sure what the notification behavior is supposed to do; I can see wanting to put off a database flush until many fields are changed, so maybe that's what it's for. The documentation doesn't tell us...

The point is that if you look at the implementation above, you see that ContentEntityBase::set() just obliterates $notify without honoring it.

That wouldn't be such a big deal because, you know, maybe this implementation needs to deal with it differently, but ContentEntityBase is the only class which implements FieldableEntityInterface.

I'm left wondering what's up.

Proposed resolution

  • Figure out whether to update the interface, or whether ContentEntityBase::set() is buggy.
  • Fix whatever needs fixing.
  • Write a test to prove that it's fixed.

Remaining tasks

User interface changes

API changes

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Mile23’s picture

Mile23’s picture

Status: Active » Needs review
FileSize
745 bytes

I discussed this with @dawehner in IRC and it's important to honor $notify because otherwise we might propagate circular notices.

Here's a patch which explores what a solution might be. Will the testbot totally barf?

Mile23’s picture

FileSize
8.03 KB
7.3 KB

Right or wrong, I wrote a test for it. :-)

Note that I had to slightly refactor the existing tests in order to isolate my test from their expectations. This is an illustration of why it is completely wrongheaded to use setUp() to set expectations.

Mile23’s picture

FileSize
7.3 KB

And with the test only, no change to set().

Status: Needs review » Needs work

The last submitted patch, 4: 2388501_4_test_only.patch, failed testing.

Mile23’s picture

Status: Needs work » Needs review
Berdir’s picture

Assigned: Unassigned » fago

$notify is about notifying the parent. ContentEntityBase/Interface never has a parent, it is always the root of the typed data structure.

What is passed on to the field is a different kind of notification flag.

$notify is probably a left-over from the type when content entities extended directly from TypedDataInterface. We can probably just remove that from the interface?

Assigning to fago to confirm this.

fago’s picture

Title: ContentEntityBase::set() ignores its interface. » ContentEntityBase::set() does not match its interface.
Assigned: fago » Unassigned

Yep, exactly! Let's remove it from the interface.

Berdir’s picture

Status: Needs review » Needs work
Issue tags: +Novice

Ok, that makes this a novice task then.

Remove $notify from ContentEntityBase and FieldableEntityInterface including the docblock documentation. Do not touch anything else.

Berdir’s picture

Category: Bug report » Task
Mile23’s picture

Category: Task » Bug report

My understanding is that we care about $notify because setting it FALSE allows you to prevent circular notifications.

Also, if we're changing the interface, it's a bug.

Berdir’s picture

Category: Bug report » Task

No, we don't care about notify and this is a task as we're not fixing a bug here, just removing a useless parameter.

rpayanm’s picture

Status: Needs work » Needs review
FileSize
1.7 KB
8.98 KB

Please review :)

Mile23’s picture

FileSize
7.23 KB
4.09 KB

Simpler code means simpler test. :-)

Berdir’s picture

Status: Needs review » Needs work

No, $notify *inside* the method needs to stay, only the parameter should be removed.

Berdir’s picture

Title: ContentEntityBase::set() does not match its interface. » $notify in FieldableEntityInterface::set() is useless
Mile23’s picture

FileSize
8.42 KB
3.29 KB

That's why I asked. :-)

Mile23’s picture

Status: Needs work » Needs review

daffie queued 17: 2388501_17.patch for re-testing.

Status: Needs review » Needs work

The last submitted patch, 17: 2388501_17.patch, failed testing.

daffie’s picture

Issue tags: -Novice

The implementation of the set() function has changed. A reroll is not enough.

Mile23’s picture

Mile23’s picture

Status: Needs work » Needs review
FileSize
8.39 KB
7.03 KB
3.71 KB

Here's a reroll of 17, and the changes I made to get it to work. The interdiff is between the reroll and the new patch.

daffie’s picture

Status: Needs review » Needs work

@Mile23: It all looks good to me. I have only one problem. In the function testSet() there is a lot of preparation for a test, but a actual test assert is never called.

Mile23’s picture

+++ b/core/tests/Drupal/Tests/Core/Entity/ContentEntityBaseUnitTest.php
@@ -388,4 +398,56 @@ public function testLabel() {
+  /**
+   * ContentEntityBase::set() does not return a value.
+   *
+   * It only has side-effects, so we have to mock everything and set
+   * expectations that way.
+   *
...
+    $mock_field->expects($this->once())
...
+      ->with(
...
+    $mock_base->expects($this->once())
...
+      ->with($name)

We could assert NULL for the void set(), but we're covered with these other expectations.

For proof you can run it with --strict. :-)

$ ./vendor/bin/phpunit --filter ContentEntityBaseUnitTest --strict
PHPUnit 4.1.4 by Sebastian Bergmann.

Configuration read from /Users/paulmitchum/projects/drupal8/core/phpunit.xml.dist

...........

Time: 5.4 seconds, Memory: 107.25Mb

OK (11 tests, 32 assertions)
Mile23’s picture

Status: Needs work » Needs review
daffie’s picture

Status: Needs review » Needs work

@Mile23: Let me start by saying that you are technically right.

I have two arguments to change it to an assertion:

  1. In #2057905: [policy, no patch] Discuss the standards for phpunit based tests there is a part about best practices. And it is saying: "Tests must pass under --strict. This means, for instance, that all tests must make an assertion."
  2. For less competent programmers like myself it is very helpful to to an assertion at the end of the function. So that it is immediately clear what is the test.
$this->assertNull($mock_base->set($name, $value));

The rest of the patch gets a RTBC for me.

Mile23’s picture

Status: Needs work » Needs review
FileSize
7.12 KB
657 bytes

it is very helpful to to an assertion at the end of the function. So that it is immediately clear what is the test.

Well, if that was the test, there'd be an assertion. :-)

But your point is taken, so here we go.

daffie’s picture

Status: Needs review » Reviewed & tested by the community

The patch removes the parameter $notify from the method set(). From the class and from the interface.
The test class is updated.
The comments are in order.
It all looks good to me.
The patch gets a RTBC from me.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/tests/Drupal/Tests/Core/Entity/ContentEntityBaseUnitTest.php
@@ -108,7 +108,7 @@ class ContentEntityBaseUnitTest extends UnitTestCase {
-  protected function setUp() {
+  protected function generateFixtures() {

I think that the test changes here are superfluous - writing tests to hit every line with an @covers can be futile since it can become more difficult to maintain the test than code. And rather than testing ContentEntityBase::set() what we should be testing is ContentEntityBase::get() since that has logic about getting translations - but such a change is out of scope.

daffie’s picture

@Mile23: I have looked at the code again and I think that it does no harm if we rename generateFixtures() back to setUp(). If I am wrong, please tell me.
What do you think about the idea of alexpott for adding testing for the method get()? In a new issue of course.

Mile23 queued 28: 2388501_28.patch for re-testing.

Mile23’s picture

@Daffie: I have looked at the code again and I think that it does no harm if we rename generateFixtures() back to setUp(). If I am wrong, please tell me.

Try it and run the test. It will fail, because the former setUp() code generates expectations for every test, which aren't met by the tests added by the patch. It's an incorrect use of setUp(). The easiest way to deal with that is to just rename it.

@alexpott: writing tests to hit every line with an @covers can be futile since it can become more difficult to maintain the test than code.

Be specific about what you *do* want, and I will make that. Otherwise, the point is to prove what the changes do, and to make those changes maintainable.

@alexpott: And rather than testing ContentEntityBase::set() what we should be testing is ContentEntityBase::get() since that has logic about getting translations - but such a change is out of scope.

So then either change scope or create another issue.

Berdir’s picture

Be specific about what you *do* want, and I will make that. Otherwise, the point is to prove what the changes do, and to make those changes maintainable.

The changes don't do anything, they remove dead code that has no meaning and can not be made to do anything.

Just upload a patch without any test changes, I don't think they are necessary here. It is not a bugfix that we need to have regression tests for.

Mile23’s picture

@alexpott: And rather than testing ContentEntityBase::set() what we should be testing is ContentEntityBase::get() since that has logic about getting translations - but such a change is out of scope.

Oh, then you want this issue: #2388537: Expand unit testing for Drupal\Core\Entity\ContentEntityBase Writing unit tests is really code analysis, wherein you find things like superfluous flags to interfaces.

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

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should 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.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should 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.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should 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.

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

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should 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.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should 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.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should 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.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.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: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should 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: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

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

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

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should 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.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should 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.

Version: 9.5.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. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.