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.

Files: 

Comments

webchick’s picture

Priority: Normal » Major

I actually think the learnability impact of this would be pretty large, so escalating to major.

yched’s picture

What 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"]

Cottser’s picture

If 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

Cottser’s picture

Issue summary: View changes

For 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.

yched’s picture

#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)

sun’s picture

Cottser’s picture

Issue tags: +minor version target

With 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.

joelpittet’s picture

There 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.

lauriii’s picture

field.html.twig was solved by only printing content

Danny Englander’s picture

This 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:

{{ item['#item'].alt }} 
{{  item.alt }} 
{{ item.attributes.alt }} 
{{ item_attributes.alt }}

... 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.

joelpittet’s picture

@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?

joelpittet’s picture

also, 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?

Danny Englander’s picture

FileSize
882 bytes
78.11 KB

@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.twig within 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.

Kint

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.

joelpittet’s picture

@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.twig file?

{% for item in items %}
    {{ item.content }}{{ kint(item) }}{{ item['#item'].alt }}
{% endfor %}
{# or outside the field template's loop #}
{{ element[0]['#item'].alt }}
Danny Englander’s picture

@joelpittet - I had not put the debug code in the loop but the figcaption with alt has been all along. Here is my field template now with the debug code in the loop:

<div{{ attributes }}>
    {% if not label_hidden %}
        <div{{ title_attributes.addClass('field-label') }}>{{ label }}</div>
    {% endif %}
    <figure{{ content_attributes.addClass('field-items') }}>
        {% for item in items %}
            {{ dump(_context|keys) }}
            {{ kint(items) }}
            <div{{ item.attributes.addClass('field-item') }}>{{ item.content }}</div>
            <figcaption>{{ item['#item'].alt }}</figcaption>
        {% endfor %}
    </figure>
</div>

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.

joelpittet’s picture

Protected

is indicating the 'visibility' of the values array 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

joelpittet’s picture

@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

Danny Englander’s picture

@joelpittet - ah, that works like a charm. I am going to play around with this some more as well. Thank you!

piyuesh23’s picture

This works great for me too.

piyuesh23’s picture

Issue tags: +#drupalgoa2015
joelpittet’s picture

Version: 8.0.x-dev » 8.1.x-dev

Moving 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.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.0-beta1 was released on March 2, 2016, which means new developments and disruptive changes should now be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.0-beta1 was released on August 3, 2016, which means new developments and disruptive changes should now be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.