Needs work
Project:
Drupal core
Version:
main
Component:
jsonapi.module
Priority:
Normal
Category:
Feature request
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
5 Jan 2020 at 00:27 UTC
Updated:
14 Aug 2025 at 22:26 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
br0kenI doubt this request will be accepted since maintainers considering a removal of the feature however this issue might become an argument since there are people using this functionality.
Comment #3
br0kenComment #4
br0kenComment #5
br0kenRe-roll for 8.9.x.
Comment #6
br0ken+ re-roll for 9.0.x
Comment #7
gabesulliceI'm not vehemently opposed to this proposal.
My opposition to the total count was entirely centered on the fact that framing the number as a "total count" is misleading and would become a source of bug reports.
However, I think I see a compromise position that I never saw before and now it seems glaringly obvious...
What if we rename
/meta/countto/meta/dataMax?I think that rename would express that the given number is an upper bound on the number of resources in a collection, not a total while providing the same information as before. I think we would need a BC-layer for the change, but the event that you've introduced could be the perfect place for an "opt-in". WDYT @BR0kEN?
Comment #8
gabesulliceOh, and as for the patch, I'm very happy about moving to an explicit API that uses the
ResourceTypeBuildEventinstead of using theResourceTypeRepositoryhack :)Comment #9
gabesulliceComment #10
jordandukart commentedI'd be a +1 towards supporting something like this as opposed to DI-ing or relying on JSON:API Extras.
Comment #11
br0kenNever thought that
countis an inaccurate word for expression the total number of items. I'd propose thedataTotaloritemsTotal.We can also make this word configurable via the same event so by default it'll stay
countuntil the next major release. In case of changing the wording should we also renamed methods/properties likeincludeCount()?Another issue right now is that the meta/count right now is not a number but string.

Comment #12
gabesulliceWhen you have access controls and you request
/jsonapi/node/article?page[limit]=10you may only get 5 resource objects in the response. Similarly, the total number of articles on your site may be 500, but if you actually request every page of the articles collection and count the resource objects in every response, you may only get 250 or even none at all. Thus 500 is an upper bound on the number of resource objects you might receive but not a total count of every item in the collection.Comment #13
br0kenProbably that is relevant to my English knowledge but I don't see how "count" doesn't fit what you described. I understand the reasoning, and "count" seems logical because it says "here is the count of items that matches current filtering criteria".
Comment #14
jordandukart commentedIf I'm interpreting @gabesullice's point correctly is that "count" is potentially misleading for people who may not be fully ingrained through the various discussion threads (https://www.drupal.org/project/drupal/issues/2942426, https://www.drupal.org/project/jsonapi/issues/2932625 and https://www.drupal.org/project/drupal/issues/2942426 for examples) or RTFMing the pagination documentation.
That is someone may assume that "count" is a reliable max as opposed to the theoretical amount of items available (assuming all access checks have passed). Since I don't imagine https://www.drupal.org/project/drupal/issues/2979294 will be seeing love any time soon I think the approach detailed here is sound beyond some semantic naming. I'll bring this up on the API-First call to try and get some more opinions.
Comment #15
br0kenDunno, `count` is still a valid name for me.
To be honest, I don't care be it `dataMax` or `numberOfItemsSatysfyingCurrentUserAccessLevelAndFilters`. Now we came to one of two most difficult things in coding - choosing the name for a *thing* (second is cache invalidation). I really hope this won't be blocking this feature for too long. I propose the maintainers to choose the name and tell me whether the code like `includeCount` properties/methods should be renamed too and I will do another patch.
Edit: can't wait for the day when D.O. will start recognizing markdown.
Comment #16
br0kenComment #17
br0kenComment #18
bbralaHi BR0kEN,
I love this change, code seems good. Could you change
counttodataMaxand then we should be ok.Also I think we should add a follow up issue to allow settings this value in the
ResourceTypeBuildEventso we expose it in an easy way.Still getting the hang on when a changerecord is required, but since this is all internal i assume it is not needed. We'll see :)
Comment #19
br0kenWouldn't it be logical to rename the property to `dataMax` and the method to `provideDataMax()`? Tbh, I really don't understand the hassle with `count`, to which everyone got used to. IMO, this `dataMax` will only bring confusion to people. I've never seen such a name for this thing.
Comment #20
bbralaSure, yeah, being constistent in the naming is a good idea.
The thing about using count is what @gabesullice already mentioned. Even though it is extremely silly and weird, as soon as any resources are not allowed for the user doing the request there can be gaps in the amount of resources. The fact is you could request, get a count of 1000 and only get 1 result in your first page or even in everything.
Personally i think this is the weirdest thing ever in jsonapi, but unfortunately this is the reality.
If we name it count, people will assume a count of objects, this will bring us problems later. Hopefully we can fix this in the future, and then we could use count. This will take a while though.
Comment #21
br0kenI started changing everything to this
dataMaxweirdy and found thatDrupal\jsonapi\JsonApiResource\DatausescountandgetTotalCount(). There is also a use of$link_context['total_count']. Would it make sense to usetotalCountinstead of thedataMax?Comment #22
soul88totalCount sounds better to me than dataMax.
Comment #23
bbralaHi @BR0kEN, i'll have a focussed look at this friday.
Comment #24
br0kenOkey-dokey, this one brings a bit of complexity but allows everyone to change the name they want to use. Of course, we'll need a CR for this and a better description in docs but for the moment I post just a patch to get the feedback.
Comment #25
br0kenCorrect a mistake that will cause the
totalCountto be always present if not explicitly disabled.With this patch, my frontend implementations that rely on
countcan stay unchanged and only backend will be affected by changing$event->setIncludeCount(TRUE)for$event->setTotalCountMetaName('count').Comment #26
bbralaI actually like the count naming, but it does mean some issues there where changes have been made already. What would happen right now to sites where TRUE is the return value there through customization, if this means requests break i think we might have a problem getting this in.
Also the patch is currently targetting 9.2.x, think this should be 9.3.x
This change is tricky. If in installations or contrib includeCount is enabled this would then change the name and break those sites. Not entirely sure how we should handle this, and if we can do this. This will at least need some explaination in an changerecord to communicatie this change.
Really do think we should investigate supporting the case where this would return true through a site/contrib change and how that would "break".
I wouldn't change this signature in the patch.
I like we can do this now.
This will either break #3105318: Add a public API for aliasing resource type names or be broken after that is merged. Something to look out for.
This is the test that kinda simulated how in contrib things are handled. Mostly jsonapi_extras though, which i also maintain.
A get_data function that also asserts status code? Seems inconsistent naming. I think just doing a decode as in other parts of this testfile is better.
Comment #27
bbralaComment #28
br0kenComment #29
br0kenincludeCount()is overridden. Really good catch!Comment #30
br0kenForgot to restore the test.
Comment #31
bbralaThanks for the work :)
I would really prefer not using this kinda function. Perhaps we can make a separate issue where we introduce a few helper methods that can do smart things like this.
Comment #32
br0kenOke.
Comment #33
br0kenComment #34
br0kenOne. More. Thing.
Sorry.
Comment #35
bbralaHmm, technically this is changing to a public API if it is included in the event.
Comment #36
bbralaThink it will help if the summary is updated according to the current solution. I've asked around if another maintainer of jsonapi will have a look also.
Comment #37
br0kenComment #38
gabesulliceLet's add
isCollectionSizeMemberEnabled()and deprecate theincludeCount()method. Then we can add an assertion that$this->totalCountMetaName !== NULLin here. That will allow us to doreturn $this->totalCountMetaName ?? 'count'. Nullable types are cool, but IMO they're easy to mishandle. Better to always return a string.I think this method should document how and why this member's value can be inaccurate.
Maybe I'm being obstinate... but let me offer this compromise: can we replace all the "total count name" keywords with the phrase "collection size member name". E.g.
s/setTotalCountMetaName/setCollectionSizeMemberNameands/$total_count_meta_name/$collection_size_member_name."Total count" and "count" have always implied a precise, discrete, and true number of resources in the collection which we cannot guarantee for weird reasons. "Size" is still that way, but a little closer to the idea of "maximum" and "limit".
Since this is a new API, we can do this:
setCollectionSizeMemberName(string $name = 'collectionSize')at the same time as we do this:getCollectionSizeMemberName() { return $this->collectionCountMetaName ?? 'count'; }. Thus anyone still overridingResourceType::includeCount()to beTRUEwill still seecountin their HTTP API. Users who upgrade (like jsonapi_extras) can callsetCollectionSizeMemberName('count')to preserve BC. Finally, new users can callsetCollectionSizeMemberName()without an argument and will get 'collectionSize'.Comment #39
br0kenWhat do you think about the solution without
isCollectionSizeMemberEnabled()? The idea is to have no repetitive logic and only usegetCollectionSizeMemberName()for determining whether to include a collection size member. ReturnsNULL- exclude, astring- include (sort of a boolean without an extra method so less to deal with). Later on we'll delete theincludeCount()and its logic becomereturn $this->collectionSizeMemberName;.Comment #40
br0kenA bit better implementation (IMO).
Comment #41
br0kenThis may be a BC for
jsonapi_extrasand similar users because previously there was a numeric string.Comment #43
br0kenComment #45
br0kenHow to make the deprecation not to fail the tests?Comment #46
br0kenComment #47
br0kenThis deprecation error trigger doesn't seem helpful as if the method is not overridden, we'll trigger it all the time the
getCollectionSizeMemberName()is called, which doesn't make sense as we know it's deprecated since we did that. If overridden, it's only possible to trigger the deprecation error if the parent is called, which doesn't seem feasible as it always returnsFALSE.What do you think of adding this assertion to the
getCollectionSizeMemberName():With that in place, we'll be triggering the error only if the method is overridden. However, this also adds an overhead of using the reflection.
Comment #48
br0kenWait, there is a better solution actually.
Comment #50
br0kenComment #51
br0kenIs it safe to delete these methods? I find them confusing as the value is provided only when the
includeCount()returnstrue(never in standardjsonapiconfiguration).Could it be that someone uses them during the normalization?
In case we can't delete them, I think they have to be deprecated in favor of
getCollectionSize(): ?intandsetCollectionSize(int $size): void.Another question: in case the methods stay, can we run the count query regardless of the meta member inclusion and always provide the collection size? Perhaps it should become the
Drupal\jsonapi\JsonApiResource\Data::__construct()argument.Comment #52
bbralaThanks for the ongoing effort you are putting into this @BR0kEN :)
To be honest I rather have a separate method to provide the check if it is enabled. This will make comparison explicit and the code more readable.
if ($collection_size_member_name = $resource_type->getCollectionSizeMemberName())We also need to be carefull for loosely typed comparisons, and that is way easier and less error prone if we have that method.
We do need to do the deprecation dance there I think. I don't see much usage in contrib, but perhaps when building custom resources this might come up. For example here in jsonapi_search_api.
Why would we run the count query if the data is not needed, that seems wastefull to do the work for something we can know we do or do not need.
Perhaps this should enclude a test for the case of providing an empty string as name. That would be invalid and we should test that case.
Perhaps this should enclude a test for the case of providing an empty string as name. That would be rather invalid.
Comment #53
br0kenisCollectionSizeMemberEnabled()will bereturn $this->getCollectionSizeMemberName() !== NULL;which seems inefficient because we'll literally callgetCollectionSizeMemberName()twice: once in anifstatement as part ofisCollectionSizeMemberEnabled()and then for obtaining the meta member name. This also expands the field to make a mistake for those override lovers because two methods must follow the same logic. Two instead of one. However, maybe I'm overengineering.assert($collection_size_member_name === NULL || trim($collection_size_member_name) !== '')that checks value correctness however I think we might also need a runtime check since it's a public API. With that guardif ($collection_size_member_name = $resource_type->getCollectionSizeMemberName())will be safe as the method returns eithernullor non-empty string.countcan easily benull(almost always if you use core'sjsonapiwith no overrides ofincludeCount()) and that the value presence depends on theincludeCount()from the resource type.IMO, these
getTotalCount()andsetTotalCount()should not exist but again, let's not touch this in scope of this issue.Comment #54
bbrala1+2:
I would actually think it would do the following:
In an if this would mean:
Which i think is way better than:
Mostly because of the loose type comparison and I rather be expicit. Technically the specification is even more specific in what would be allowed there. But lets not go there :)
Comment #55
bbralaComment #57
bbralaOpened a merge request to 9.3.x with patch from #50.
Comment #58
bbralaStill needs work though. :)
Comment #59
bbralaI've revisited this issue and concluded my comment in #54 is a bit overly defensive. I've only added a small test for the empty string and rebased for you.
Changes looking good, setting RTBC. Adding tag for CR
Comment #60
quietone commentedThis still needs a change record, setting to Needs Work for that,
Comment #61
bbralaNot sure if it's OK to RTBC again, i've added a proper changerecord.
Also; added credits for @gabesullic and @JordanDukart for research and work on this issue.
Comment #63
larowlanhiding patches because there's an MR here
Comment #64
br0kenJust migrated to 9.3.0 + the latest patch from the MR. All good. +1 to RTBC.
Comment #65
lauriii#38.2 seems to be still unaddressed.
Should we at least document the potential security concerns this could introduce on sites, given that the count bypasses
Entity::access? There are many cases where revealing that something exists is considered as an access bypass.Comment #66
bbrala@lauriii, I think that comment was addressed. The changes in the naming of the variables was applied as suggested by @gabesullice.
This is no new information that is supplied. When you currently query jsonapi the access check will be done AFTER collecting the entities for a page on the index endpoint. This means if you paginate by 10 you could have only 8 actual results (or actually 0 unfortunately). So you know that there are 2 entitites that are filtered out by the accesscheck. So this means this issue does not expose more information than it did before, since you can just count the amount of entities that are not shown in the index endpoints and get the same information.
Setting back to RTBC since I think the issues raised by @laurii have been adressed and there is no new security implications.
Comment #67
lauriiiThe first paragraph asked for additional documentation which was the part I thought wasn't addressed:
Where is this information available? I wasn't aware that JSON:API exposed that information anywhere.
Comment #68
bbralaHmm, i just had a look at the documentation and found:
Filters and Access Control
There is mention of access checks being done after the fact and that has those implications. But i cant seem to find an explicit mention in the docs right now regarding the lists that could have gaps.
I've also gone through security considerations but haven't really found an explicit mention.
I'd be happy to work on improving the documentation, but perhaps that could be in a child/followup?
Comment #69
quietone commentedI've switched this to 10.0.x because it needs to be applied there first, can we get a 9.5.x version too,
@bbrala, thanks for adding the changed record. It doesn't look like that has been reviewed. Can someone do that?
Comment #70
immaculatexavier commentedRerolled patch against 10.0.x
Attached rerolled diff
Comment #71
bbralaThanks for the reroll! Seems the reroll didn't go as planned though unfortunately.
Comment #72
immaculatexavier commentedRerolled patch against 10.0.x
Attached rerolled diff
Comment #73
bbralaGreat, thanks! Unfortunately there is a styleissue that needs to be fixed in ResourseType.php
We also need a review on the CR I wrote.
Comment #74
immaculatexavier commentedRerolled patch against 10.0.x, Addressed #73
Attached interdiff
Comment #76
immaculatexavier commentedRerolled patch against #74
Attached interdiff
Comment #78
immaculatexavier commentedRerolled patch against #76
Attached interdiff
Comment #80
bbralaI've rerolled to 10.0.x, there were no conflics which is lovely. Only made a small change to the deprecation message. I think this change was minimal so I still stand by my RTBC.
We still need a review on the changerecord though as mentioned in #69 by @quietone
Comment #81
daffie commentedReview for the CR only:
Can we add a link.
For the MR: I do not think we can remove public methods without deprecating them first.
Comment #82
bradjones1This class, like most of all of json:api modules', is marked internal so this is not strictly a BC break.
Comment #83
daffie commentedGood point. Than they are not part of the public API and they can be removed without deprecating them first.
Comment #84
bbrala#81.1: From the changerecord: "The JSON:API ResourceTypeBuildEvent now has a new method:
setCollectionSizeMemberName(string $name = NULL). Subscribers to that event can now enable the collection count and set the member name used as key in the meta data of the lists."#81.2: We could add a link, but then it needs to be merged first. I'll promise to add that :)
#81.3: I've added the method to the CR.
#81.MR: When doing a codesearch is seems to see no usage that will break. The code will check the resources for an implementation and use that if it exist. So it does seem fine.
Comment #85
daffie commented@bbrala: Thank you for your reply.
All my points have been addressed and the CR is RTBC for me.
Comment #87
alexpottI've added some questions to MR.
I think we might need another CR to detail all of the code changes. The current CR focuses on the new stuff which is great but I think we should have CR that covers the API changes and what someone who has used the old API is supposed to do.
Comment #88
bbralaOk, went through the feedback and think i addressed all of it. More BC code, a new BC test, and a new change record.
:)
Comment #89
wim leersphpstan&phpcsare not yet happy.Comment #90
bbralaOops, thanks.
Comment #91
bbralaHmm, that is really unfortunate. So i cannot use a contructor promoted property because phpstan doesn't want that? :(
Comment #92
bbralaRemoved the property promotion since that means a snakecase class property, that doesn't fly in drupal afaik :(
Also added an ignore for phpstan for a anonimous testclass.
Comment #93
bbralaI've rebased the MR onto 10.1.x-dev since it was no longer mergable.
Comment #94
bbralaCleaned up the rebase, there were smoe release commits in there for some reason. Don't know why. Fixed by doing an interactive rebase on itself and dropping the extra commits. Diff from 10.1.x now looks clean <3
Comment #95
gaurav_manerkar commentedThere is a merge conflict for
core/phpstan-baseline.neonComment #96
gaurav_manerkar commentedComment #97
br0kenMeh, now I can't apply this sweety patch https://git.drupalcode.org/project/drupal/-/merge_requests/1308.diff to my Drupal 9.4.x 😔. The thing with patches seems more reliable
Comment #98
br0kenIn case someone is looking for a 9.4.x version.
Comment #99
bbralaFixed phpstan error and rebased while im at it.
Comment #100
smustgrave commentedMR 1308 had a CI failure.
Did not test or review.
Comment #105
bbralaLets try again, updated to 11.x and fixed tests <3
Comment #106
smustgrave commentedCan the issue summary be updated with a clear proposed solution?
Deprecations appear right and trying to test locally
Applied the MR
Created some articles
Went to /jsonapi/node/article
But not noticing any difference with or without the MR. Any guidance on how to test would be appreciated.
Ping me or I'll keep an eye on this to come back around and prioritize a faster review so it doesn't sit.
Comment #107
volegerAre you sure the meta key name has to be `total_count`?
According to the JSONAPI example https://jsonapi.org/examples/#pagination there is a reference to `totalPages`.
Comment #108
bradjones1The spec says: