Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Given an entity such as a $node, I have to do this to get the bundle entity:
$node_type = entity_load($node->getEntityType()->getBundleEntityType(), $node->bundle());
Could we add a method to ContentEntityBase to do this?
Comments
Comment #2
bojanz CreditAttribution: bojanz at Centarro commented+1
Comment #3
martin107 CreditAttribution: martin107 commented+1
Comment #4
joachim CreditAttribution: joachim commentedHmm 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.
Comment #5
joachim CreditAttribution: joachim commentedComment #7
joachim CreditAttribution: joachim commentedComment #9
joachim CreditAttribution: joachim commentedComment #10
Crell CreditAttribution: Crell at Platform.sh commentedThis 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.
Comment #11
joachim CreditAttribution: joachim commented> 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.
Comment #12
Crell CreditAttribution: Crell at Platform.sh commentedIt'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? :-)
Comment #13
joachim CreditAttribution: joachim commented> 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.
Comment #14
joachim CreditAttribution: joachim commented> 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.
Comment #15
dawehnerThis 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.
Comment #16
bojanz CreditAttribution: bojanz at Centarro commentedYes, 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.
Comment #18
joachim CreditAttribution: joachim commentedSeeing 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?!?!?
Comment #19
joachim CreditAttribution: joachim commented> 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:
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)
Comment #20
joachim CreditAttribution: joachim as a volunteer commented> 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?
Comment #21
tim.plunkettMight as well use
$this->entityTypeManager()
There's \Drupal\KernelTests\Core\Entity\EntityBundleFieldTest, probably a good starting place.
Comment #22
joachim CreditAttribution: joachim as a volunteer commentedMade 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...)
Comment #23
tim.plunkettWouldn't recommend var_dumping objects. I'd use xdebug.
Comment #24
joachim CreditAttribution: joachim as a volunteer commented> 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.
Comment #26
tim.plunkettdebug() 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.
Comment #27
jibranShould we add a new interface for this? or we can add a trait and use it in
ContentEntityBase
.Comment #28
joachim CreditAttribution: joachim as a volunteer commentedOn https://www.drupal.org/core/d8-bc-policy it says:
Comment #29
tim.plunkettIf you can sell it to committers, that's great. But I've had little luck with that.
Comment #30
joachim CreditAttribution: joachim as a volunteer commented> 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.
Comment #31
tim.plunkettbeStrictAboutOutputDuringTests 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).
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.Comment #32
dawehnerIs it a config entity or not, this is the question :) Maybe we should adapt the interface?
Comment #33
joachim CreditAttribution: joachim as a volunteer commentedDo you mean that the return type hint should be the config entity interface?
I'll update with changes on #31 and #32 later today.
Comment #34
dawehnerMaybe? 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.
Comment #35
joachim CreditAttribution: joachim as a volunteer commentedCore 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.
Comment #36
joachim CreditAttribution: joachim as a volunteer commentedMade 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...?)
Comment #37
dawehnerMaybe a potential webform solution might use content entities as bundle, so yeah let's go with that.
Comment #38
chx CreditAttribution: chx at Smartsheet commentedI 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:
What about here?
Comment #39
joachim CreditAttribution: joachim as a volunteer commentedI 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?
Comment #40
tim.plunkettIf 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.
Comment #41
chx CreditAttribution: chx at Smartsheet commentedI 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.
Comment #43
joachim CreditAttribution: joachim as a volunteer commentedTest fail doesn't look related to this patch.
Comment #44
joachim CreditAttribution: joachim as a volunteer commentedComment #46
joachim CreditAttribution: joachim as a volunteer commentedStill doesn't look like this issue is related to the test fail.
Comment #47
catchI 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?
Comment #48
joachim CreditAttribution: joachim as a volunteer commentedBut 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.
Comment #49
catchI 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.
Comment #50
joachim CreditAttribution: joachim as a volunteer commentedOnly thing I've found in core is in Content moderation, which has this method:
which is only ever called like this:
Comment #51
joachim CreditAttribution: joachim as a volunteer commentedI'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.
Comment #52
joachim CreditAttribution: joachim as a volunteer commentedThe 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:
with a trait:
The latter doesn't look as nice!
Comment #53
tim.plunkettWell,
public function getBundleEntity();
would be on a new interface. So the new example would beComment #56
JacobSanford@joachim's patch in #36 no longer applied to 8.5.x. A reroll with no further modifications is attached.
Comment #57
martin107 CreditAttribution: martin107 commentedJust want to see what testbot has to say.
Comment #59
claudiu.cristeaUse this and you're done :)
Comment #60
joachim CreditAttribution: joachim as a volunteer commented#59 is very elegant, but it still requires you to know a fair bit about the internals.