Problem/Motivation
The JSON:API FieldResolver
was written prior to a lot of internal JSON:API refactoring. The biggest of which was the introduction of the ResourceType
value object which helps map Drupal's entity-based data model into a JSON:API resource object-based data model.
Part of the field resolver has already been refactored to use this value object, but FieldResolver::resolveInternalEntityQueryPath($entity_type_id, $bundle, $external_field_name)
has not been.
This shortcoming came up during Decoupled Days 2019 when a lot of interest was expressed for adding the ability to fetch all entities of a particular entity type, not just per-bundle. In other words, JSON:API does not currently have a /jsonapi/node
route, only /jsonapi/node/{bundle}
routes.
It was agreed that an experiment would be made in contrib to support his new feature. See https://www.drupal.org/project/jsonapi_cross_bundles. This refactoring would unblock that experiment in contrib (currently it requires a core patch/hack) while making the JSON:API module more internally consistent.
Proposed resolution
Refactor this:
FieldResolver::resolveInternalEntityQueryPath($entity_type_id, $bundle, $external_field_name)
into this:
FieldResolver::resolveInternalEntityQueryPath(ResourceType $resource_type, $external_field_name)
which would make that method match the already refactored method on the same class:
FieldResolver::resolveInternalIncludePath(ResourceType $resource_type, array $path_parts, $depth = 0)
User interface changes
None.
API changes
None. The field resolver is @internal
.
Data model changes
None.
Release notes snippet
N/A
Comment | File | Size | Author |
---|---|---|---|
#26 | 3070204-26.patch | 11.9 KB | gabesullice |
#26 | interdiff-25-26.txt | 1.84 KB | gabesullice |
#15 | 3070204-15.patch | 11.74 KB | ravi.shankar |
#7 | 3070204-7.patch | 11.7 KB | gabesullice |
#7 | interdiff.txt | 6.54 KB | gabesullice |
Comments
Comment #2
gabesulliceComment #3
gabesulliceComment #4
gabesulliceHere is the contribute project issue for tracking this change.
Comment #5
gabesulliceComment #6
gabesulliceComment #7
gabesulliceComment #9
mglamanSo weird, I can't see comment #7.
➕This makes the method more cohesive with the rest of the code to abstract away the concepts of entity types and bundles to just resource types and resource objects.
Comment #10
gabesulliceI must have accidentally unpublished my comment. It should be back.
All I did was fix the broken tests. There aren't any really significant changes.
Comment #11
e0ipsoThis looks good. It's a nice little refactor with real world impact. I'm good with the code as is.
I'll let @mglaman RTBC this since he'll be using it in the
jsonapi_cross_bundles
module.Comment #12
mglamanPer my thoughts in #9, pushing the RTBC button :).
Comment #13
Wim LeersMarking
for this.🙈 It was "for", now it is "from". I'll let a native speaker decide :) Curious to learn what's correct!
🥳 Nice, this is a small performance improvement.
🤔This is more complicated than before. Why do we need to change this? Why was the field resolver previously satisfied, but not anymore?👍 Ahh, because the field resolver was itself loading the resource type from the repository again, where it did contain all the necessary information rather than only this thin, incomplete stub of it.
Comment #14
Wim LeersI forgot to say: I love the simplification here! 🤩
Comment #15
ravi.shankar CreditAttribution: ravi.shankar at OpenSense Labs commentedI have done this patch on Drupal 8.8
Comment #16
gabesulliceBack to RTBC per #13.1.
#13:
1. Thank you @ravi.shankar for re-rolling the patch!
2. Both are correct, the previous was correct because the field wasn't resolved from the entity type and ID. Since the resource type was loaded and it was derived from that. It's correct now because the field is resolved from the given resource type (via
getRelatableResourceTypes()
). Although, you could make the case either way, in both instances.3. 👍
4. Exactly right.
Comment #17
Wim LeersRTBC++ :)
Comment #18
larowlanshould we be providing a BC layer here.
Yes its marked internal but this is a hard-break that could cause issues for people during the minor update.
We can do this in a BC fashion using
func_get_args
andinstanceof
checks, and trigger a deprecation error if the old signature was calledComment #19
mglamanEverything about JSON:API in every release has been creating breaking changes for anyone daring to touch it's PHP "API". As an outsider who has been fiddling for a while, it has been a constant following of BC as I touched @internal classes and methods. I don't think we need to add backward compatibility layer code debt.
Comment #20
gabesullice+1! Also, many of the same arguments I made in #3014277-55: ResourceTypes should know about their fields apply here as well. Including the comment about JSON:API Extras, which provides an
EntityToJsonAPI
service which already handles the complicated and error-prone work of constructing a JSON:API response with includes.Comment #21
Wim LeersAgreed with #19 and #20 🙂
Comment #22
jibranFWIW, I'm not a huge fan of adding BC just for the sake of it but I think in this case ignoring it won't hurt.
As per http://grep.xnddx.ru/search?text=Drupal\jsonapi\Context\FieldResolver and http://grep.xnddx.ru/search?text=resolveInternalEntityQueryPath, the only other module extending this class or using this function is https://www.drupal.org/project/jsonapi_cross_bundles which is not even overriding this method and maintained by API first team anyway.
This exact point goes against module being stable in core. As contrib maintainer myself, I haven't extended JSON:API to provide new functionality but I do maintain DER and changes in entity system API only breaks tests for that module and not the actual functionality and wherever it breaks the functionality entity system API maintainers are generous enough to provide BC. @mglaman my point here is that you shouldn't have to do these changes anymore because JSON:API has been added as a stable module in the core.
I do understand
\Drupal\jsonapi\Context\FieldResolver
is marked as an internal class but given we have a case in contrib extending this class and in future, there are chances to have more modules like this popping up we should consider removing internal here. This can be a follow-up discussion and in no way holds this issue back.Comment #23
Wim LeersA module being stable does not mean every single implementation detail is public API surface. Modules that choose to override internal APIs consciously do that and accept the responsibility of having to update. This is not special, this is not Drupal-specific. Every operating system has this concept.
In fact, this issue is a stepping stone towards the JSON:API module providing a public PHP API (see #3032787: [META] Start creating the public PHP API of the JSON:API module), because it removes some coupling of JSON:API to entities. We need JSON:API to be able to expose non-entity data eventually, and this helps with that.
Comment #24
larowlanFor the BC question
Comment #25
catchThis looks like a case where it would be easy to provide bc via func_get_args(), at the cost of not introducing the type hint. However I think @jibran in #22 is saying that the only contrib module that might have overridden or called this method isn't doing so. If that's the case we could probably skip it but it does seem easy to add compared to the other two issues.
Comment #26
gabesulliceIn the interest of just getting this committed and since it's a blocking a contrib project, let's just add the relatively simple BC layer. I sincerely don't believe any contrib is relying on this, but I suppose some crazy custom module might be.
I created #3078045: Remove BC layer from Drupal\jsonapi\ResourceType\ResourceType::resolverInternalEntityQueryPath() and a change record draft.
Comment #27
catchComment #28
Wim LeersComment #29
alexpottCommitted 12b4761 and pushed to 8.8.x. Thanks!
Discussed the lack of test with @catch we agreed that this is okay because the new code path is tested and since this method is
@internal
BC is provided on a best effort basis.Fixed the coding standard on commit.