Problem/Motivation
[This patch] fixes it for
Fileentities, NOT file fields! That was the original scope of the issue. The problem to this day, however, is that you cannot get the URL for a File entity at all. And file fields always
point to a File entity. Therefore, I think changing the normalization of file fields (by adding a computed property to file fields) is a nice-to-have follow-up. This current scope of the patch is the must-have. The current patch allows one to follow a file field's reference to a File entity, get the File entity, and in there the file URL can be found.
This is that nice-to-have follow-up issue.
Proposed resolution
Remaining tasks
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #31 | field_image.png | 13.8 KB | berdir |
| #27 | 2925520-27-interdiff.txt | 1.91 KB | berdir |
| #27 | 2925520-27.patch | 5.6 KB | berdir |
| #24 | 2925520-24.patch | 3.7 KB | berdir |
| #19 | 2925520-19.patch | 1.07 KB | wim leers |
Comments
Comment #2
wim leersComment #3
wim leersThis came up again in #2825487-162: Fix normalization of File entities: file entities should expose the file URL as a computed property on the 'uri' base field.
Comment #4
berdirThis reminds me of my idea somewhere to automatically expose the entity label of a reference field in a REST response.
Of course, that again reminds my of my recent argument that adding computed properties to field types isn't free.. it results in always instantiating a class (a plugin, going through typed data manager/factory and so on) currently, even when it is not accessed.
Comment #5
wim leersYep, I turned that idea in a comment of yours into an actual issue: #2913518: Expose labels of referenced entities 😀
Comment #6
wim leersLooks like we have consensus that this is one of the next steps, see
🎉
Added this to the top priorities at #2905563-37: REST: top priorities for Drupal 8.5.x.
Comment #8
wim leers#2825487: Fix normalization of File entities: file entities should expose the file URL as a computed property on the 'uri' base field landed a long time ago, this hasn't been blocked since then!
Comment #9
tedbowOk I am kind of confused with the issue summary.
We could add a 'url' property to
\Drupal\file\Plugin\Field\FieldType\FileItem::propertyDefinitionsBut I am pretty sure this would be overwritten in normalization because in
\Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer::normalizeSo this would overwrite any 'url' property, correct?
Is 'url' what this issue was meant for adding anyways?
Here is the output I get already:
So do we still need this issue?
Comment #10
berdirHm, interesting problem.
The thing is that url is using the deprecated url() method. And only that url method returns the return to the physical file as it does not actually have a canonical link template. So if we'd at the same time convert it to use toUrl() if there is a canonical link template, then it would continue to work as-is for files and other entity types and we would not result a change while getting rid of the deprecated all and custom override of file entity.
The only problem is when combining with file_entity which does add a canonical link template, then we'd end up with overwriting it again. What if we expose this as file_url? Means we can't remove the deprecated call for file, but we could officially deprecate it, possibly with a hidden setting like other things we did?
Comment #11
tedbowSo if we did this the REST response for FileItems would have
\Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer::normalize()\Drupal\file\Plugin\Field\FieldType\FileItem::propertyDefinitions()So without File Entity installed these would both be the same value? It seems like that would be confusing to consumers. How would they they know which one to use?
Comment #12
berdir'url' for file entities would only be exposed anymore if
serialization.settings:bc_expose_file_item_url(or something like that) would be set, which like other settings, would only be set to TRUE by default on existing installations.Comment #13
wim leersIndeed… 🤐
This time, it's not #2402533: Provide File::createFileUrl() as a replacement for the deprecated File:url() implementation that gets in the way, but the default Drupal normalization.
This sounds splendid.
Can't you do a pre-emptive release of
file_entityfor 8.5.x that detects the Drupal core version and if so, behaves differently on 8.6, to account for this change landing in Drupal 8.6? Because thefile_entitymodule is filling a gap in Drupal core. Once core no longer has that gap, the need for thefile_entitymodule disappears (I know the module does much more than only providing acanonicalroute though.) This is similar to how thejsonapimodule was (and still is) adding a computedurlfield: Drupal 8.5 is shipping with the fix for that, so the need for the JSON API-specific work-around disappears. So the module needs to adapt (and it has to do/maintain less 🎉).Just trying to propose a pragmatic approach with long-term, slow evolution here.
I can live with that.
Right, the unfortunate thing is that #2277705: Files don't have URI nor href introduced a hacky work-around rather than solving the root of the problem (which this issue is solving).
I think as part of this issue, we should remove
\Drupal\file\Entity\File::url()(i.e. revert #2277705: Files don't have URI nor href). But of course, there are BC implications to that :( That's what #2402533: Provide File::createFileUrl() as a replacement for the deprecated File:url() implementation is trying to deal with. Also see #2922487-19: Follow-up for #2910211: fix all deprecation warnings — we have to fix the deprecation warnings in the REST module, but to do so, we have to solve the wholeFile::url()problem. Because\Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer::normalize()is triggering a deprecation warning by calling the deprecatedEntityInterface::url()method, but we need to call that to not break BC forFile! It's tough.You meant , right?
Comment #14
wim leersComment #15
wim leersOh and #2907402: HAL normalization of file fields don't provide file entity id or file entity REST URL is related too.
Comment #16
wim leersAnd yet another issue wrt the same problem: #2701941: On File entities $entity->url('canonical') returns an absolute path rather than a relative one like all other entities.
Comment #17
berdirBehave differently how? file_entity really *has* a canonical link template, at /file/ID, which is where you can view the file entity just like you can view a node at /node/ID, with the fields.
If we go with file_url then this becomes a non-issue.
Also, I think file_entity should really be officially marked as deprecated now in favor of media, but it will not go away any time soon.
As (I think?) I already wrote, the thing is that url() already *is* deprecated. We don't need to remove it now, we can just let it stay a behavior specific to url(), which will be removed in in 9.x, and mark #2402533: Provide File::createFileUrl() as a replacement for the deprecated File:url() implementation as a won't fix.
The harder part is indeed getting rid of the call in the normalizer. That's what I meant with:
> Means we can't remove the deprecated call for file, but we could officially deprecate it, possibly with a hidden setting like other things we did?
By setting, I mean we would add another bc setting to serializer that would skip that call, similar to hal.settings:bc_file_uri_as_url_normalizer (that's why I would have liked to put both behind the same setting, as they are closely related, now we need two different settings).
So as usual with those things, existing sites continue to export url and file_url as the same thing. New sites only expose file_url. If you install file_entity, you also get the url as a real canonical link.
Comment #18
wim leersSorry for the confusion!
Ahh! I definitely didn't catch that, sorry! Do you mean it'd only skip that call for the
Fileentity type?Comment #19
wim leersSo, something like this. (Just meant to be a starting point.)
Comment #20
berdir> Do you mean it'd only skip that call for the File entity type?
I think we'd simply make it trigger using toUrl()->toString() instead of url(), which would then automatically no longer call the special url() method on file entity. So no, it would not be specific, we need to get rid of that call for all entity types.
Not sure if we want to name it specifically about file entity or something more generic.
Comment #24
berdirRe #19, yes basically that, but ComputedFileUri is specifically for the file uri base field and gets it from the field parent, while here we want to get it from the referenced entity in the file, small but important difference (->entity vs. getEntity(), yes I know how confusing this is). So I had to add a second class.
Working on the test fails, we've made a bit of a mess.
So in #3009854: Fix "The "serializer.normalizer.file_entity.hal" normalizer service is deprecated: it is obsolete, it only remains available for backwards compatibility." deprecation error, I pointed out that there is no file url left after that change. That is correct, but it's only correct for hal_json I believe, "regular json" has a fallback in \Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer::normalize(), but that doesn't apply to hal_json. Yes, I totally get the irony that this is the point that Wim always makes and I'm reluctant about (adding everything on the typed data level instead of normalization because then it works for all formats).
The relevant code was changed in #2922487: Follow-up for #2910211: fix all deprecation warnings but it was kept like that as it was before for BC, and it has a @todo pointing here, except that we'd like to add here here with a different property. I guess we'll have to name it url then here to continue that BC :-/.
Also interesting that ComputedFileUri explicitly makes it root-relative whlie the existing code there is absolute, so to keep BC there too, we need to make ComputedFileItemUri and ComputedFileUri inconsistent :-/.
So I tried to make that BC by renaming to just url and adding it as an absolute URL.
Also, something about the Hal tests is weird... I think extra properties in entity reference fields are still broken there. Fact is that the Hal tests weren't failing unlike Json and XML based tests and also now, no changes seem to be necessary, when they actually should fail as we are supposed to be adding extra stuff to the output. Double-irony or something, extra computed properties only work if the normalization layer actually cares about that... :)
Comment #25
berdirComment #27
berdirGetting that jsonapi test working was pretty icky, the hardcoded file ID's for example are pretty tricky to get right. Also hardcoded ids in general is a bad idea, because it will fail on environments that skip autoincrement values. Would be better to load the files based on a unique filename instead. But I went with the existing logic in the test.
Also the flow is sometimes quite hard to follow, which test fail is getting its values from where, that's the downside of those generic tests.
Also, seems like it would be useful to test with actual values for display/description in that file field.
Comment #29
jibran#3066751: Add resolvable_uri property to LinkItem for APIs is doing the same thing for the link field. @Berdir do you think #3066751-8: Add resolvable_uri property to LinkItem for APIs is an explorable option?
Comment #30
berdirSome new things that I found while looking at this again and also things to check/discuss based on the new direction of #3009854: Fix "The "serializer.normalizer.file_entity.hal" normalizer service is deprecated: it is obsolete, it only remains available for backwards compatibility." deprecation error.
That logic there will automatically be applied to hal_json normalization as well, which is good. But, regular json doesn't have that reusable logic and has hardcoded/duplicated it, with another file fallback as mentioned earlier. I would assume it would only make sense to apply that behavior from hal_json also to json, possibly even as the same setting, if we move the BC setting up into serialzation.
If we do that, then we basically also lose the file URL in json, just like we do now in hal_json. But that might actually be a good thing because we do then not have to respect the existing behavior and can instead make the file url on file/image item an the file entity consistent: file_url property, root-relative URL.
Just manually confirmed that, steps to reproduce:
1. Install standard
2. Enable rest, hal, basic_auth (to get the optional default config imported). Optionally allow cookie in rest.resource.entity.node
3. Create an article with image and alt text.
4. Visit node/1?_format=hal_json
You don't see the alt text. And the file obviously also doesn't contain it. That is because \Drupal\hal\Normalizer\EntityReferenceItemNormalizer::normalize() specifically only does the embedded uuid + lang + self/link. But we also need to include the stuff from \Drupal\hal\Normalizer\FieldItemNormalizer::normalizedFieldValues() (again minus target_id) somehow. top-level, new nested key? I think in _embedded doesn't make sense.
So my regular question.. how is anyone using this for something real if you can't possible access alt text, file description, ...
If we can find a name for a setting we could possibly bring that all under one BC setting. Because we do need all of it to eventually be able to have consistency and direct access to file URL's on node/media entities. I'd vote for "serialization_file_insanity: true". I'll show myself out now...
Comment #31
berdirWe could actually add those extra attributes like any other field
Still needs some work, but this:
Results in this:
Needs some more cleanup at least to get rid of empty entries with nothing but the langcode (the url is coming from the patch here)
Comment #33
wim leersLet's do that then.
+1
Right.
This sounds great! 👍 Not perfect, but at least consistent :)
Heh … this is what I've been saying for a while… and it's why I pushed for #3049856: [policy] Mark HAL module as deprecated in D9 so it can be removed in D10 a while ago 😅
😭
Sorry that this isn't more helpful. I can do a more helpful comment tomorrow. 🤞
Comment #34
berdir> #31: I don't think dealing with those other properties in this issue is in scope of this issue. But if it makes things easier, then … sure!
I wish. file_uri is a property too. So either we deal with it generically, or we special case file_uri now. Either way, we need to do *something*.
Comment #35
berdir#24: Yeah, that comment is the result of hours of debugging I think, my brain might not have been fully sane anymore at that point. Take your time with that comment ;)
See updates on #3009854: Fix "The "serializer.normalizer.file_entity.hal" normalizer service is deprecated: it is obsolete, it only remains available for backwards compatibility." deprecation error and #2907402: HAL normalization of file fields don't provide file entity id or file entity REST URL, I think this is 9.1 material at this time, so we can take our time with it. Or if we decide by then to remove hal.module from core, which might not be the worst idea. As mentioned before, my only use case is default_content.module, and as discussed before, it might more sense for that to just do its own format that just has the raw content, we don't need any fancy normalization, $entity->toArray(), a bit of metadata and converting that to json or yaml should mostly do the trick :) The main thing to figure out is the dependency tree, we'll need to keep those referenced-entity uuid's something. That's the number #1 selling point of default_content compared alternatives.
Comment #36
wim leersI frankly think this is the simplest, sanest course of action.
default_contentdoes not need any URIs at all. There is an issue for that already at #3049856: [policy] Mark HAL module as deprecated in D9 so it can be removed in D10. Would you mind leaving a comment there?If you're switching to that line of thinking … then can you please post a comment there?
Comment #37
wim leers#34: I think it's preferable to special-case
file_urithen.#24:
I still can't make sense of this. #3009854 is only about the HAL normalization and hence only about theAh, this is drawing the parallel betweenhal_jsonformat. So I don't know how to interpret the "as well" in the first sentence.\Drupal\hal\Normalizer\FileEntityNormalizer::normalize()'s BC logic and\Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer::normalize()'s special case forfileentity references I think. Then yes, I think you're right, we want the same setting, and it needs to apply to both.Correct!
We could do that. JSON:API already does something like that:
It puts the additional properties that live on that entity reference (a "relationship") that aren't solely about the reference (alt, width, height, title in the case of a
@FieldType=image) under themetakey (which is how the JSON:API spec allows adding extra information to relationships).We did that to avoid making the data computed in
ComputedFileUrisite-specific: we didn't want to make it have to vary by theurl.sitecache context. If we want to keep BC for the existing HAL normalization, then we can just make the HAL normalization do extra work: make it detect if a relative URL is being returned, and if so, prefix it with the current request's hostname + port + subdir.Comment #38
berdirWill try to find some time to get back to this after the two related issues are done.
One comment on this though:
> default_content does not need any URIs at all.
That isn't quite so simple. It doesn't use URI's anymore, but it very much requires content relationships. That's the most important functionality of default_content IMHO. It builds a dependency tree of all content that is to be imported, and makes sure that it creates terms referenced by nodes first, authors of nodes also first, comments on those nodes afterwards and so on.
That's why hal+json was chosen as a format, because it provides the necessary information for that. Initially it used URL's but that was changed to using the UUID's that are provided for each relationship.
Comment #46
claudiu.cristea+1 for this. It should be similar with "processed" computer property from rich text fields.
Comment #47
claudiu.cristeaThis module would benefit from such a new property, see #3446770: How to map image field?