Support from Acquia helps fund testing for Drupal Acquia logo

Comments

joachim created an issue. See original summary.

bojanz’s picture

+1

martin107’s picture

+1

joachim’s picture

Hmm not sure what to do about adding this to an interface.

Also, bundle-related methods are on the base Entity class, even though config entities don't have bundles. So this should probably follow the same pattern, and just return NULL on Entity.

joachim’s picture

Status: Needs review » Needs work

The last submitted patch, 5: 2699835-5.drupal.entity-get-bundle-entity.patch, failed testing.

joachim’s picture

Status: Needs work » Needs review
FileSize
2.29 KB

Status: Needs review » Needs work

The last submitted patch, 7: 2699835-7.drupal.entity-get-bundle-entity.patch, failed testing.

joachim’s picture

Status: Needs work » Needs review
FileSize
3.3 KB
Crell’s picture

Status: Needs review » Needs work
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -93,6 +93,15 @@ public function getEntityTypeId();
+   * @return \Drupal\Core\Entity\EntityInterface|null
+   *   The config entity which is the bundle of the entity, or NULL if the
+   *   entity does not have bundle entities.
+   */
+  public function bundleEntity();

This should be on ContentEntityInterface, therefore, since it's only relevant to content entities. A stubbed-out method on some subclasses indicates that the method is too high in the inheritance tree. And "null if it doesn't apply" is a code smell, without question. :-)

Also, I feel like this should be a get*() method, but that's more debatable.

joachim’s picture

> A stubbed-out method on some subclasses indicates that the method is too high in the inheritance tree. And "null if it doesn't apply" is a code smell, without question. :-)

I was aligning this with the bundle() method, which is on EntityInterface. But yes, it does seem a bit wrong.

Made that change, and the name change as well, since we have getEntityType/getEntityTypeId/etc.

Crell’s picture

It's still something-or-null, although a few other pieces of the patch seem to be missing? The last bit in particular, in the ViewsUIObjectTest. Was that not important? :-)

joachim’s picture

> although a few other pieces of the patch seem to be missing? The last bit in particular, in the ViewsUIObjectTest. Was that not important? :-)

That was necessary when the method was on EntityInterface. Now it's moved up, that's no longer needed.

joachim’s picture

> It's still something-or-null,

I think it's still possible for a content entity to opt not to use config entities for its bundles. I heard of Commerce doing something like that with (possibly?) its payment method entities.

dawehner’s picture

This code still assumes there is always a bundle entity type, which is not true for users for example. Maybe some nice little test would be cool as well.

bojanz’s picture

Yes, Commerce uses plugin bundles in a few places, so the bundle ends up being the plugin id. In which case the bundle entity method simply needs to return NULL.

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.

joachim’s picture

Status: Needs review » Needs work

Seeing to needs work in light of:

> This code still assumes there is always a bundle entity type, which is not true for users for example.

> Maybe some nice little test would be cool as well.

and also:

> + $bundle_entity_type = $node->getEntityType()->getBundleEntityType();

$node??? What was I thinking?!?!?

joachim’s picture

> And "null if it doesn't apply" is a code smell, without question. :-)

Working on this to cover the case for user entity, I see that this is a code smell that's already there:

  $user_entity_type = $\Drupal::service('entity_type.manager')->getDefinition('user');
  // Returns NULL.
  $user_entity_type->getBundleEntityType();

Users have no bundle entity type, and so getBundleEntityType() returns NULL (though it's not documented that it can do so! -- #2801551: getBundleEntityType() can return NULL)

joachim’s picture

Status: Needs work » Needs review
FileSize
1.61 KB

> Maybe some nice little test would be cool as well.

I had a look at where this might go, but the tests I see for the entity system don't appear to have anything as complex as a content entity & bundle entity combination. Any suggestions on where this could go?

tim.plunkett’s picture

+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -433,6 +433,17 @@ public function bundle() {
+      $bundle_storage = $this->entityManager()->getStorage($bundle_entity_type);

Might as well use $this->entityTypeManager()


There's \Drupal\KernelTests\Core\Entity\EntityBundleFieldTest, probably a good starting place.

joachim’s picture

Made a start on a kernel test, but I can't figure out how to get any output from the test while I develop!

debug() crashes the test (without giving a good explanation). print_r() outputs nothing. var_dump() of two entities brought my whole machine to its knees and I had to kill the test (something which Devel Kint does easily...)

tim.plunkett’s picture

Wouldn't recommend var_dumping objects. I'd use xdebug.

joachim’s picture

> Wouldn't recommend var_dumping objects. I'd use xdebug.

In which case I'm going to have to leave writing the tests to someone else.

I've *once* managed to get xdebug working (after a DrupalCon session that walked through the setup) and about a month later it broke.

With young kids at home, my non-work coding is done in short stretches of usually only an hour or so, and I just don't have the time or the energy to go down the rabbithole of trying to get xdebug working.

I'm attaching the test file with as far as I got -- it doesn't work, and I can't figure out why.

Status: Needs review » Needs work

The last submitted patch, 24: EntityBundleEntityTest.diff, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
3.01 KB
2.98 KB

debug() uses trigger_error() to print it's HTML message, which causes the test runner to fail. Which is good, errors are bad. Also it prints HTML, which is intended for a GUI runner like Simpletest, not CLI like PHPUnit.

Additionally, tests will fail whenever a test causes output to be printed.


The test was failing because the 'entity_test' entity type has no bundles. Switching to using 'entity_test_with_bundle' works fine.
Added an assertion and cleaned up the rest of the test.


There's one big problem though. Adding methods to ContentEntityInterface is a BC break, we need an alternate approach here.

jibran’s picture

Title: add a method to ContentEntityBase for getting its Bundle entity » Add a method to ContentEntityBase for getting its Bundle entity

There's one big problem though. Adding methods to ContentEntityInterface is a BC break, we need an alternate approach here.

Should we add a new interface for this? or we can add a trait and use it in ContentEntityBase.

joachim’s picture

On https://www.drupal.org/core/d8-bc-policy it says:

Interfaces that are not tagged with either @api or @internal can be safely used as type hints. No methods will be changed or removed from these interface in a breaking way.
However, we reserve the ability to add methods to these interfaces in minor releases to support new features.

tim.plunkett’s picture

If you can sell it to committers, that's great. But I've had little luck with that.

joachim’s picture

> debug() uses trigger_error() to print it's HTML message, which causes the test runner to fail. Which is good, errors are bad. Also it prints HTML, which is intended for a GUI runner like Simpletest, not CLI like PHPUnit.

That's a shame. I'm sure debug() used to work with the old Drush test-run command.

> Additionally, tests will fail whenever a test causes output to be printed.

That must be a Drupal thing. I'm using PHPUnit for the tests for Drupal Code Builder, and my local copy has the test classes littered with print_r()s. Test produce a ton of output, but pass!

Thanks for writing the test!

It occurs to me that if the entity_test entity wasn't working because it doesn't have bundle entities, then it would be suitable for testing that case where we expect a NULL return. Here's that added to the tests.

tim.plunkett’s picture

beStrictAboutOutputDuringTests is a native PHPUnit feature, it can be controlled by your phpunit.xml. The default file we ship with, phpunit.xml.dist, has it enabled. (Also beStrictAboutTestsThatDoNotTestAnything and beStrictAboutChangesToGlobalState).

+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityBundleEntityTest.php
@@ -0,0 +1,60 @@
+    $this->assertEquals(NULL, $obtained_bundle_entity);

If anyone wants to rewrite this, it can be assertNull($obtained_bundle_entity). Also usually in unit tests we'd split the second half into a new method.

dawehner’s picture

+++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
@@ -23,6 +23,15 @@
+   * @return \Drupal\Core\Entity\EntityInterface|null
+   *   The config entity which is the bundle of the entity, or NULL if the

Is it a config entity or not, this is the question :) Maybe we should adapt the interface?

joachim’s picture

Assigned: Unassigned » joachim

Do you mean that the return type hint should be the config entity interface?

I'll update with changes on #31 and #32 later today.

dawehner’s picture

Do you mean that the return type hint should be the config entity interface?

Maybe? I was wondering whether there is a usecase of having a bundle defined as content entity. For a second I thought feed and feed item have this kind of relationship, but they don't have it.

joachim’s picture

Core seems to allow that a bundle entity *could* be content:

> protected function loadBundleDescriptions(array $bundles, EntityTypeInterface $bundle_entity_type) {

> * The name of the entity type which provides bundles.

joachim’s picture

Made changes from #31, and removed mention of config entity from the docs.

(Interdiff seems a bit odd, as surely the install schema in the tests was in the last patch...?)

dawehner’s picture

Status: Needs review » Reviewed & tested by the community

Maybe a potential webform solution might use content entities as bundle, so yeah let's go with that.

chx’s picture

I can't claim to fully understand what this patch covers or not and the same can be said about #2799785: Entity types with non-config bundles can not be moderated but the two sure looks related:

entity types whose bundles are not managed by a config entity are out of luck and can not be moderated.

What about here?

joachim’s picture

I don't see how that's related to this.

An entity type whose bundles aren't config entities still has the Entity->bundle() method, which returns the bundle name.

What should we do here for these entity types?

tim.plunkett’s picture

If the bundle is a config entity, it will work just fine.
If it's not, you'll need to override the new method and do something of your own design.

chx’s picture

I am fine, I was just doing usual RTBC patrol and saw that issue and was like 'never heard of an entity type before which had bundles but not config entities, odd' and then this and was like 'hrm, I wonder what happens'. Nothing more.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 36: 2699835-36.drupal.entity-get-bundle-entity.patch, failed testing.

joachim’s picture

Test fail doesn't look related to this patch.

joachim’s picture

Status: Needs work » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 36: 2699835-36.drupal.entity-get-bundle-entity.patch, failed testing.

joachim’s picture

Status: Needs work » Reviewed & tested by the community
Update.Drupal\system\Tests\Update\UpdatePathTestBaseFilledTest

fail: [Browser] Line 248 of core/modules/system/src/Tests/Update/UpdatePathTestBase.php:
GET http://localhost/checkout/update.php/start?id=4&op=do_nojs returned 0 (0 bytes).

Still doesn't look like this issue is related to the test fail.

catch’s picture

Status: Reviewed & tested by the community » Needs work

I can see this is useful, but I think this would better off in a new trait instead of ContentEntityBase. Not all content entities have bundles, not all of those with bundles use config entities for them.

Also is there anywhere we can use this in core?

joachim’s picture

But there is already EntityTypeInterface::getBundleEntityType() -- the possibility of entities having bundles and those being themselves entities is present throughout the core entity component.

If we want to clean that up then fair enough, but then should we file an issue to move that to a trait too? I feel this is unfairly holding this issue up.

> Also is there anywhere we can use this in core?

I'll have a poke around.

catch’s picture

I think EntityTypeInterface::getBundleEntityType() should also move to a trait as well yeah. It's too late to remove it from the interface, but it could be deprecated and moved into a new interface at least.

joachim’s picture

Only thing I've found in core is in Content moderation, which has this method:

  protected function loadBundleEntity($bundle_entity_type_id, $bundle_id) {
    return $this->entityTypeManager->getStorage($bundle_entity_type_id)->load($bundle_id);
  }

which is only ever called like this:

loadBundleEntity($entity->getEntityType()->getBundleEntityType(), $entity->bundle())
joachim’s picture

I've made a start with adding a trait, but I think it's not ideal: presumably you mean that ContentEntityBase wouldn't use it, but Node, Comment, Term, and so on would. The problem I see here is that it makes this a whole lot less useful, because then a module like Content Moderation can't call it on a generic content entity.

joachim’s picture

The more I think about it, the more I think that a trait doesn't provide good DX.

Suppose I am working with general content entities. With a method, I do this:

if ($bundle_entity = $entity->getBundleEntity()) {
  // Do things with the bundle entity
}
else {
  // Fallback
}

with a trait:

if (method_exists($entity, 'getBundleEntity') {
  // Do things with the bundle entity
}
else {
  // Fallback
}

The latter doesn't look as nice!

tim.plunkett’s picture

Well, public function getBundleEntity(); would be on a new interface. So the new example would be

if ($entity instanceof BundleEntityWhateverInterface) {
  $bundle_entity = $entity->getBundleEntity();
  // Do things with the bundle entity
}
else {
  // Fallback
}

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.

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.

JacobSanford’s picture

@joachim's patch in #36 no longer applied to 8.5.x. A reroll with no further modifications is attached.

martin107’s picture

Status: Needs work » Needs review

Just want to see what testbot has to say.

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.

claudiu.cristea’s picture

Status: Needs review » Closed (won't fix)

Use this and you're done :)

$bundle_entity = $entity->get($entity->getEntityType()->getKey('bundle'))->entity;
joachim’s picture

Status: Closed (won't fix) » Active

#59 is very elegant, but it still requires you to know a fair bit about the internals.

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.