Problem/Motivation
Most bundle entities don't allow deletion if there are entities of that bundle. For example, you can't delete the 'article' node type if there are 'article' nodes.
The following form classes which enforce this logic are almost identical:
- BlockContentTypeDeleteForm
- MediaTypeDeleteConfirmForm
- NodeTypeDeleteConfirm
All these bundle entity delete forms are reinventing the wheel, and contrib bundle entity types are having to do this too.
(Note that vocabulary entities are different -- deleting a vocabulary causes the deletion of all the terms.)
Steps to reproduce
Proposed resolution
Provide a generic BundleEntityDeleteForm class. In most cases, this could be used as-is by bundle entity types.
Given that we have CommentTypeDeleteForm which has additional checks for allowing deletion, I suggest we have:
- buildForm() is generic and reusable
- the actual logic goes in checkMayDelete(), which returns an array of reasons for disallowing deletion. This is what CommentTypeDeleteForm could override.
Remaining tasks
Needs change record review, of both change records.
User interface changes
Before with JS

Before no JS

After with JS

After no JS

Introduced terminology
API changes
A new BundleEntityDeleteForm is provided for entity types using bundle entities.
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|
Issue fork drupal-3571580
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
joachim commented@ben280398 I'd started working on this too BTW.
Comment #4
ben280398 commentedGot it @joachim. Don't want to step on any toes here so I'll work on something else man.
Comment #5
ben280398 commentedGot it @joachim. Don't want to step on any toes here so I'll work on something else instead man
Comment #6
ben280398 commentedDon't want to step on any toes here @joachim, so I can work on something else, no worries man :) (also my previous comments seem to have disappeared so this is attempt #3)
Comment #8
joachim commentedThis is the sort of approach I was thinking.
Still to convert: content block and media.
Comment #9
joachim commentedWe can remove some tests too -- things like this
in NodeTypeTest and maybe elsewhere should be moved to one of the test entity types.
Comment #10
joachim commentedThe following tests can be replaced by a generic test:
testBlockContentTypeDeletion
MediaUiJavascriptTest
NodeTypeTest.php
Comment #11
joachim commentedComment #12
joachim commentedComment #13
joachim commentedComment #14
joachim commentedComment #15
joachim commentedComment #16
berdirI think if we're going to generalize this, then it would be useful to allow to delete the content at this point. See \Drupal\paragraphs\Form\ParagraphsTypeDeleteConfirm for example, for paragraphs, this is more important than others, because there's no UI for them, and if there are for some reason some leftovers then it can be hard to delete them.
It would need to be done in a way that scales, so a batch or so, paragraphs doesn't do that yet.
Comment #17
joachim commentedWhat Paragraphs has is this:
Adding this to core is increasing the scope of this issue by quite a bit! But I can see that the current way I've made the form wouldn't leave space for this.
Not sure how best to proceed.
Comment #18
longwaveWouldn't we want two separate form classes here, so you can opt in to either functionality? Perhaps we add the "delete entities" button in a followup, although we likely want to take it into account when naming the class here I think.
Comment #19
joachim commentedI don't yet see how the delete entities thing would work with the getReasons method I've added.
Comment #20
longwaveMaybe keep a "reasons" method for additional reasons, but have a separate method for counting
bundleOfentities? Only allow the delete if the count is non-zero but there are no additional reasons?Comment #21
joachim commented> Maybe keep a "reasons" method for additional reasons, but have a separate method for counting bundleOf entities?
Yeah that's what I'm thinking so far.
> I think if we're going to generalize this, then it would be useful to allow to delete the content at this point
We already have 'delete-multiple-form' link templates and an DeleteMultipleForm. Should this be expanded to allow a bundle route parameter? But it's really extending the scope.
Comment #22
longwaveMaybe you can link off to the
delete-multiple-formfor thebundleOfentity, and setdestinationback to the parent entity delete form? That makes it two explicit steps instead of one.edit: oh, but we're only deleting a bundle, not the entire entity type, so that needs extending too :/
Comment #23
joachim commented> edit: oh, but we're only deleting a bundle, not the entire entity type, so that needs extending too :/
Yeah.... I think that extending the entity delete system should be a follow-up, which we can leave space for here.
Comment #24
joachim commentedNeed some opinions, as I can see two ways to do this:
A. We add a button 'Delete all content entities', and handle the submission of that in BundleEntityDeleteForm.
B. We make part of the 'You may not remove this @bundle-label until you have removed all of the %type @plural-content-label.' text a link to the content entity type's 'delete-multiple-form' link template. Adding the link would be done as a follow-up, as Drupal\Core\Entity\Form\DeleteMultipleForm does not yet support bundles.
I sort of prefer B, as adding a separate place where deletion is done feels messy.
Comment #25
joachim commentedReady for review.
Comment #26
sivaji_ganesh_jojodae commentedThe Pipeline has failed with following error,
Comment #27
joachim commentedFixed.
Comment #28
smustgrave commentedAppears to be 3 open threads if those could be answered.
This is looking really awesome btw!
Comment #29
joachim commentedRealised that we need to keep the Cancel button, for users without JS, as they will see this form on its own page rather than a modal.
Comment #30
smustgrave commentedDid a rebase for the random failures and it's all green
But do agree with the open threads can we add the test coverage back?
Comment #31
joachim commentedRestored the entity-specific tests.
But I really don't see the point -- we have a test with a generic entity type. So what is the point of testBlockContentTypeDeletion() -- it's a whole functional test, a significant amount of resources, which just repeats what BundleDeleteFormTest now does for the generic case. What is this for?
Comment #32
smustgrave commentedBelieve that was the only feedback LGTM
Comment #33
acbramley commentedTests are failing, also think we should add a couple more lines to the entity_test coverage.
Comment #34
joachim commentedFixed the tests failures.
Comment #35
smustgrave commentedChanges look good to me, before I mark though @Joachim did you want to convert to a kernel? I don't have an opinion.
Comment #36
joachim commentedCould do -- is @acbramley happy with the new test not submitting the form?
Comment #37
smustgrave commentedThanks @joachim and @acbramley for following up in this on slack. Feedback appears to be addressed to me. LGTM
Comment #38
catchOne question on the MR.
Comment #39
joachim commentedDone.
Comment #40
smustgrave commentedFeedback from catch addressed
Comment #41
quietone commentedI read the comments and the change record. I didn't find any unanswered questions and I updated credit. What I didn't find is a review of the change record..
And this is adding a form so should be tagged, usability, per the Usability Core Gate. I am adding the tag now. And as the UI is being changed there should be a screen shot of the change or changes.
I applied the diff and did limited testing but enough to have a before and after screenshot. In the after screenshot I was surprised to see the period at the end of the title as other similar forms do not include a period. Only a '?' if the title is a question. The new form also has two ways to cancel, one at the upper right and the Cancel button.
Back to the change record. The change record is written for developers. But this change also affect site builders and admins so it should include a sample screen shot etc for those users.
Comment #42
joachim commented> In the after screenshot I was surprised to see the period at the end of the title as other similar forms do not include a period. Only a '?' if the title is a question.
Good point. I can remove it but I can't do a new screenshot on my current system.
EDIT: full stop removed.
Comment #43
quietone commentedThanks for the prompt wee change. I uploaded a new after screenshot. I do think it looks better.
Comment #44
joachim commented> The new form also has two ways to cancel, one at the upper right and the Cancel button.
The reason for this is that if you don't have JS, instead of a pop-up, you are taken to the form on a page of its own. In that case, there is no modal title bar with an [x]. Without a cancel button, the only way back would be the browser Back button, which is not good UX.
Comment #45
catchIt might be worth a separate change record for the UX change? Something like 'Form structure of bundle entity delete forms has changed' with the screenshots. Have only done a quick re-review of the code but overall it looks encouraging.
Comment #46
quietone commentedI have added a new CR based on the last comment and added screen shots in it. That will need a review.
I also updated the existing CR to change the tense to be the present tense, as in this is what it does, not what is should do.
Comment #47
quietone commentedActually, maybe needs work to move the cancel button to the right in the nojs version?
Comment #48
joachim commentedThe form structure CR looks fine.
> Entities which are bundles of another entity type will not be deleted if there are entities of that bundle
This is incorrect. They *should not*. If you don't guard against this, your form CAN delete them and then your site is broken.
Comment #49
smustgrave commentedCR looks fine to me also, believe that was the last think?
Sorry @joachim fell off my radar
Comment #52
catchSorry this took a while to get back to. Committed/pushed to main and 11.x, thanks!
Comment #54
godotislateThis broke an MR pipeline against 11.x: https://git.drupalcode.org/project/drupal/-/jobs/12917008
core/tests/Drupal/KernelTests/Core/Entity/BundleDeleteFormTest.php has the wrong namespace:
Drupal\FunctionalTests\Entity\BundleDeleteFormTest. It should beDrupal\KernelTests\Core\Entity\BundleDeleteFormTest.We probably also need to look into why that isn't being detected on main.
Comment #57
godotislateMR with fix: https://git.drupalcode.org/project/drupal/-/merge_requests/17465
MR against 11.x to show tests pass: https://git.drupalcode.org/project/drupal/-/merge_requests/17467
Comment #60
catchCommitted/pushed the follow-up to main and 11.x - we have an issue open about the failure to detect namespace mis-matches, although can't find it right now.
Comment #62
godotislate@catch were you thinking of #3612498: PhpUnitApiFindAllClassFilesTest not finding namespace errors? That one got in last month, but it looks like doesn't cover this case.
Comment #63
catch@godotislate - yes that one, and that's annoying.
Comment #64
godotislateOpened #3631345: PhpUnitApiFindAllClassFilesTest still not finding all namespace errors.