Problem/Motivation
This has come up in a few Twig conversions (cite specific ones?), and recently on Drupal StackExchange: http://drupal.stackexchange.com/questions/97876/printing-field-item-attr...
There is a mismatch between Render API and Twig. If you want to get a value from a render array that's nested within a hash key the syntax is ugly:
{{ item['#item'].alt }}
OR
{{ attribute(item, '#item').alt }}
Proposed resolution
Extend Twig to allow for a much nicer and more intuitive syntax for accessing the same data:
{{ item.item.alt }}
I'm not sure how expensive this would be but maybe when drilling down we can check if ['#item'] exists if ['item'] is not found. This would only be needed for render arrays.
Remaining tasks
- Patch
- Tests
User interface changes
n/a
API changes
This would be an API addition of sorts.
| Comment | File | Size | Author |
|---|---|---|---|
| #13 | image-alt-kint.png | 78.11 KB | danny englander |
| #13 | dump-keys.txt | 882 bytes | danny englander |
Comments
Comment #1
webchickI actually think the learnability impact of this would be pretty large, so escalating to major.
Comment #2
yched commentedWhat if both '#foo' and 'foo' keys exist ?
# and no# keys have vastly different meanings in render arrays (property vs. children / raw arbitrary data vs. rendered html). Isn't it a bit confusing/misleading to merge them into a unique syntax ?
What about a different dereferencing syntax for #properties ?
element.child
element:property
(or even element#property ?)[edit: bleh, Twig reads # as "comment"]
Comment #3
star-szrIf both #foo and foo keys exist then you would have to use the
{{ item['#foo'].alt }}syntax if you want #foo.It would be nice to avoid inventing new Twig syntax for Drupal, so far we've been able to keep standard Twig syntax and just add custom tags, filters, functions, etc. and that would be the idea behind this kind of helper.
However if we consider this just being a documentation issue: http://twig.sensiolabs.org/doc/templates.html#variables
Comment #4
star-szrFor the record, you can still use the dot operator after attribute(), so I've added that example to the issue summary.
Edit: And to be clear, I think the real fix for this issue would be to rework render API to actually be an API, but that's not D8 material. This is just something to ease our pain in the meantime.
Comment #5
yched commented#2042773: Change #items within a theme_field() render array from an *array* to the same $items *object* used throughout the rest of the formatter pipeline is related - it's about deciding what you find in $element['#items'].
FWIW, field formatters placing raw values in $element['#items'] is here only so that the theme layer can use it. So we can totally adjust this according to what makes most sense downstream. As long as it's part of the renderable array though, it has to be in a # property (it's data, not html)
Comment #6
sunComment #7
star-szrWith beta 1 out, it seems to me like this could/should be a minor version addition so we can get 8.0.0 out the door.
Comment #8
joelpittetThere is a large problem that I don't think we can solve (getting rid of render arrays with their '#' keys and adding them to renderable objects D9).
Then there is more localized issues. Maybe we can be a bit more specific and tangible in the issue summary.
It sounds like this is related to field.html.twig specifically. Possibly we could wrap the field item in a decorator/proxy object to help expose those keys, though I think that may be a significant performance hit?
I guess we won't know until we try. Tagging as sprint for focus.
Comment #9
lauriiifield.html.twig was solved by only printing content
Comment #10
danny englanderThis method no longer seems to be working in Drupal 8, Beta 3. Turning on Twig debug and Devel Kint does not shed any further insight either.
In my custom field template, I've tried:
... but none of those work. Kint prints out a lot of stuff including my alt tag but none of it seems useful in terms of drilling down to get this to render in the template. Twig debug basically just shows template suggestions. So I am not sure what I am doing wrong here.
Comment #11
joelpittet@Danny Englander I'm not sure if I'm being too obvious but 'item' is a placeholder some "renderable array". Depending on which template you are in that variable name will change. Within any template you can get a list of the available variables when you have twig_debug turned on by means of
{{ dump(_context|keys) }}which if I remember right kint() will be nicer output compared between d7's var_dump() vs dpm() respectively.If the variable is a "renderable array" the most keys will be '#whatever' which makes it (like javascript) necessary to use the square brackets to access those keys instead of dot notation. Which is the aim of this issue to hide or remove hashed keys from twig. OOP renderable objects would be ideal but that has been pushed to D9 because of how drastic that change would be.
Does that help or confuse further?
Comment #12
joelpittetalso, I really like your gratis theme and I think I just rattled off things you already know, so sorry about that. Can you post your field templates dump that includes the alt tag so we can see the structure you are getting?
Comment #13
danny englander@joelpittet - I don't know much of this, still learning so any help is really appreciated! Glad you like Gratis.
The file I am working in is
field--field_image.html.twigwithin Gratis. I've attached a text file that has the contents of my{{ dump(_context|keys) }}from within the aforementioned file. Also attached is a screen capture of kint. Note with kint, I am trying to drill down by using{{ kint(element.0) }}so what you see here is just below that level. With{{ kint() }}, there was only one instance of my alt tag.In the past, this worked in this file to render a caption based on the alt tag:
<figcaption>{{ item['#item'].alt }}</figcaption>-- but that's what is no longer working. Note that I was the Stack Exchange OP which prompted this issue to open here.
Comment #14
joelpittet@Danny Englander huh, well that should work, if you follow ImageItem to FieldItemBase class it has magic methods for getting those protected values...
And you are definitely putting that in the "items" for loop? Would you mind also posting the
field--field_image.html.twigfile?Comment #15
danny englander@joelpittet - I had not put the debug code in the loop but the
figcaption with althas been all along. Here is my field template now with the debug code in the loop:Still no joy after
drush cr. I still can't make heads or tails of Kint, every path has "protected" in it so I am not sure if that needs to preface my code like{{ content.item['#item'].protected.alt }}or something like that.Comment #16
joelpittetis indicating the 'visibility' of the
valuesarray property. Meaning you aren't supposed to access that directly BUT, that object has magic __isset() and __get() methods on it that access those properties for you... SO what you have should work. I'll give it a try and see.@see http://php.net/manual/en/language.oop5.visibility.php
Comment #17
joelpittet@Danny Englander turns out I am to blame for this:S
Anyways here's how you can get it:
{{ item.content['#item'].alt }}The reason is that
{{ item }}used to print the content. To keep it in line with other changes for attributes it was needed to separate the value from the attributes around them for some consistency.Here is the issue that moved those around on ya: #2229435: Clean up the way attributes are printed in field.html.twig
Comment #18
danny englander@joelpittet - ah, that works like a charm. I am going to play around with this some more as well. Thank you!
Comment #19
piyuesh23 commentedThis works great for me too.
Comment #20
piyuesh23 commentedComment #21
joelpittetMoving this to 8.1.x because there is no real headway or patch on this major and it's tricky without OOP renderable objects and our render array hashes.
Comment #26
pranali.addweb commentedProvide {{ item.item.alt }} Twig syntax for getting data from $item['#item']['alt']
Here we want to print the alt tag as we know Drupal 8 twig does not deal with hashtags while printing a specific array or variable. In twig Basically, # is used to place a comment. SO if we want to fetch the alt using {{ item.#item.alt }} would not work.
So a perfect solution to print this variable from that given array is by using {{ item.item.alt }}. I am just mentioning this because this is working for me. Like for an another example if we want to render $content['#foo']['alt'] we should use {{ content.foo.alt }} to fetch the value from the content array.
Comment #28
sun–1 All of these magic access methods make it substantially harder for newbies to learn Twig templating in Drupal. The problem is not the access, the problem is the deep structure itself. Drupal urgently needs to simplify its data structures instead.
Comment #29
star-szrAgreed, it would just be a band-aid solution so I am also -1.
Comment #41
smustgrave commentedThank you for creating this issue to improve Drupal.
We are working to decide if this task is still relevant to a currently supported version of Drupal. There hasn't been any discussion here for over 8 years which suggests that this has either been implemented or is no longer relevant. Your thoughts on this will allow a decision to be made.
Since we need more information to move forward with this issue, the status is now Postponed (maintainer needs more info). If we don't receive additional information to help with the issue, it may be closed after three months.
Thanks!