Closed (fixed)
Project:
Entity Usage
Version:
5.x-dev
Component:
Code
Priority:
Normal
Category:
Bug report
Assigned:
Unassigned
Reporter:
Created:
17 Sep 2025 at 15:57 UTC
Updated:
1 Jul 2026 at 08:20 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
alexpottHere's what you'll see on the usage tab if you follow the instructions in the issue summary:
I think the problem here is the it is quite confusing what each column is referring to.
If you don't delete the paragraph but instead just remove or change media reference you'll see
And here you get a used in column which refers to the fact the media item is used in old revisions of the paragraph entity.
I'm not sure what the fix is but I think we need to be more clear about what each column is referring to as from user testing this completely confused people QAing a site I'm working on.
Comment #3
berdirThis a known issue and there's only so much that can be done about this with the current architecture. Published is actually the paragraph I think and not the node, unless there's custom code for that too.,
entity usage tracks paragraphs. And then the usage table attempts to backtrack that paragraph reference to the host entity which can involve multiple jumps, each of which can no longer be in active use. As in, the paragraph references to it's host, but the current revision of that host does no longer use that paragraph.
The code for that predates the "Used in" column and wasn't adjusted for that. It should be possible to improve it a bit and report it in the same way, but it will always be a complicated and slow approach that requires working up the parent tree and comparing the back-reference against the actual reference.
A long time ago, I proposed a different approach to tracking usages where such composite entities that don't really exist on their own are all assigned directly to the host, but that requires API changes to the plugin structure, see #3002332: Track composite (e.g. paragraph) entities directly and only as their host. There's some work there that I haven't reviewed yet.
Comment #4
berdirAh, partially misremembered. The previous revision bit actually comes from paragraphs, in its label method: \Drupal\paragraphs\Entity\Paragraph::label().
entity_usage would need to duplicate similar logic into the list controller that generates that table to pull that out of the label and instead display it in the Used in column.
Comment #5
alexpottThere is custom code for. this - see \Drupal\entity_usage\Controller\ListUsageController::getSourceEntityStatus() where it does:
Comment #6
marcoscanoI agree that the root of the issue is that under the hood we track 1:1 all relationships without distinction, but on the UI we don't want to display all of them in the same way. This distinction makes the controller code complex, inefficient and prone to errors and edge cases.
That said, I agree there is a bug here we can fix to improve editorial experience of the scenario you describe. IMO the fix should be to:
1- Add the "Used in (Old revision's)" column also in your scenario 1
2- (unrelated, we can do this in a follow-up) Hide the "Status" column whenever the "Used in" column is displayed (or when the usage doesn't refer to the default revision of the host). To me it makes no sense to display "Published" or "Unpublished" for the host when the usage isn't in the default revision.
We may surface other edge cases by doing so, but I agree this is likely a step forward in improving things.
Comment #7
berdirAgreed with the suggestion, you essentially need to add more paragraph specific checks, don't call label()
On 2, I think what that essentially means is combining Status and Used in into a single column, as it can be a mix of both for different usages. I think "Status: Used in old revisions" is a valid thing to say as "Status"
Comment #8
alexpottDiscussed this with @daniel.bosen. Here's what I think we should do for paragraphs...
1. The entity title column should be the host entity title unless the paragraph is orphaned.
2. The status column should be removed.
3. The type column should be paragraph type on host type (unless orphaned)
4. The usage column should always be present and we should list where the entity is used in which revisions of the host entity via the paragraph.
5. If the paragraph is on the default revision and references the entity and the host is published then then the used in text will contain "Published revision"
For non paragraph usages we:
1. Remove the status column
2. Always are the used in column and if the entity reference is used on the default revision and the default revision is published then the text will contain "Published revision".
Comment #9
berdirMostly makes sense, I'm not sure I fully follow your proposal in regards to status vs used in columns. "Published revision" doesn't add much value to me over just "Published" and you didn't really specify what it would say for a default revision that isn't published. I'd consider just a merged version that is Published/Unpublished/Old revision(s).
I also saw a recent comment from someone that the paragraph location bit in the title is actually useful information for them and I agree that it can be on longer, more complex entities. If we're going with custom logic for that, it could be a secondary row below the title, something like:
Linked Node Title
Paragraph: Container > Card
That's a pattern we use pretty often in our admin views to add more info without having too many columns.
Comment #10
marcoscanoI agree with the 2-row suggestion above, I too think that displaying the paragraph chain somehow is useful information for editors.
As for the status/used in column, I think any solution that combines them will improve things, and the wording should in all cases refer to the top-level host entity, not the immediate parent entity.
Comment #12
alexpottThis issue could not be tackled without determining the target revision ID at entity usage save time. Well it could for paragraphs because we can go back and do a query to work this out. But it could not be done for non-reusable blocks placed using layout builder. This is because that information is embedded in a mass of serialized PHP. So I had to change tack at that point and when we are registering usage for paragraphs and content blocks embedded in layouts we record the revision ID in the entity usage table. We can then use this information to pair paragraph or content block with the correct revision of the thing (usually a node) that contains them.
I've also implemented what will probably turn out to be quite a big performance improvement on the entity usage tab in that once we work out that an entit is used in old revisions we stop processing once we will not display any further information. We can do this because we don't display any information about with older revisions the entity is used on.
In order to add the column to the entity usage table we need to rebuild it. Therefore the update path just drops and recreates the table with the new schema. For this reason I think this improvement belongs in a new major version of the module. What do you think @berdir and @marcoscano.
Comment #13
alexpottComment #14
alexpottHere's where I'm currently at. To test this:
On HEAD this is what you see:

With the MR:

Comment #15
alexpottSo the one bit of information that you no longer get on the MR version is the paragraph type. But also the paragraph type information is incomplete even in HEAD because you don't know what the paragraph type is that has the paragraph field. Also the MR version is telling you the node type which I think is probably more useful in this context than the paragraph type.
@berdir what do you think?
Comment #16
marcoscanoI like the new approach, sounds good enough for most use cases 👍
Comment #18
marcoscanoComment #20
alexpottSummarising a conversation between myself, @marcoscano and @berdir from a while back. We were discussing how to generalise the approach so the we can do the same for inline content entity blocks as the current MR is doing for paragraphs.
1. What is the best name for these plugins that allow an entity usage to determined by the host entity rather than itself? The best options were
HostEntityUsageInterfaceorInlineEntityUsageInteraface2. At the moment I’m stopping the user from being able to select paragraphs in the entity usage setting screen. Does this make sense? Should we ignore the case where a user has added a regular entity reference to paragraphs. If you create such a field in the UI you see a message saying Note: Regular paragraph fields should use the revision based reference fields, entity reference fields should only be used for cases when an existing paragraph is referenced from somewhere else. Should we allow the user to track that use-case? If so should we allow paragraphs to be selected on the UI but not track the case where a paragraph is referenced from an ERR field?
3. At the moment the UI allows you to enable and disable plugins but I think plugins that implement this new interface should not be able to be enabled or disabled by the user. They should be able to decide themselves - for example the ERR plugin should be enabled if the entity_reference_revisions module is installed.
4. How should we handle ERR fields to something other than a paragraph? Berder pointed out ERR itself doesn't hardcode paragraphs. it relies on the additional entity type definition keys for parent type/id to treat it accordingly, as does tmgmt. entity_usage could do the same, but maybe there are paragraph specific bits in the current language. the methods on paragraph entity at least aren't on an shared interface.
5. We already have hook_entity_usage_block_tracking for custom modules to implment complex requirements for when not to track. But I think we should replace that API with hooks from \Drupal\entity_usage\EntityUpdateManager::allowSourceEntityTracking() and \Drupal\entity_usage\EntityUpdateManager::allowTargetEntityTracking() as this would allow you to bail before doing the work. Imo this is a separate issue.
Given the discussion I'm going to try to abstract out the current approach to paragraphs and implement for inline content blocks so we can prove the new API can do this. Should have the side effect of fixing #3181406: Wrong text when entity is layout builder revision here.
Comment #21
alexpottAfter discussions with @daniel.bosen and trying to fix the generic case I've gone back to making this issue only deal with the paragraph case. Doing the inline block case meant we'd have to load some target entities to check if they were inline and this feels wrong / something that will be easier to explore in follow-ups. This change now fixes things for entity types that paragraphs which really are a special field on another entity type and never should be considered as an entity from the perspective of the user.
@daniel.bosen and I also had a good discussion about the the used in and status column. The "Status" column was removed. Its information is now part of the "Used in" column on the entity usage list page.
Each cell in the "Used in" column contains one or more of:
When a row contains more than one of these, they render as a bulleted list.
Comment #22
cgmonroe commentedI know it's an old issue, but just saw this.
FWIW I had to deal with this a bit with my embedded paragraphs code. #3504984: Support embedded paragraphs/ Improve paragraph usage info I took the approach that embedded paragraphs and paragraphs were not linkable (i.e. no viewable /entity-type/id links) and should not be the endpoint displayed on the usage report.
I added some code to the getSourceEntityLinks to recurse thru the source info to find an actual linkable entity. So that if you had a trackable entity in a paragraph in an embedded paragraph on a node the usage info would show: Node title (linked) > paragraph. I think the status was based on the linkable parent so it would be 'old revision'
The other benefit of this is that content people want to know were to edit the location that uses the thing they need to change. Telling them a paragraph without a link doesn't work. Being able to go to the node and drill down does.
Comment #23
marcoscanoNice work @alexpott 👏
I agree with dealing with paragraphs only for now, and the implementation doesn't hardcode things too strongly, we just happen to have a single implementation of
::getInlinePluginIds(), we can always expand that later.Also agreed on the "Used in" simplification 👍
I went through the MR and haven't found anything worth improving, except for a minor nit. I have also tested this manually on a vanilla install and everything looks right to me.
When we merge this, I believe we should also mark #3002332: Track composite (e.g. paragraph) entities directly and only as their host as done, since that's effectively what's happening here.
Being this 5.x only, I think it's fine to get it in, and have real sites start to test this out slowly as the 5.x introduction. Feel free to merge when you think this is ready. Again, nice work!
Comment #25
alexpott