Score:3

How can I build a render array that results in a string rather than a Drupal\Core\Render\Markup object?

in flag

I'd like to use Twig Tweak's drupal_view() to render a view in my paragraph template. I have a couple entity reference fields on the paragraph and need the IDs of the referenced entities passed as contextual filter arguments.

I built a Contextual Filter String field formatter to format the contextual filter string with the hope that it could be passed directly as the argument's value. The formatter will return all if the reference field is empty, otherwise it will return the referenced entity IDs as a string with AND/OR separators, e.g. 1,2,3 for AND or 1+2+3 for OR.

Inside my template, I'm attempting to render the view with the following twig:

{% set arg1 = content.field_arg1|render %}
{% set arg2 = content.field_arg2|render %}

{{ drupal_view('my_view', 'my_display', arg1, arg2) }}

But this does not work!

Rather than strings, my arg variables are actually Drupal\Core\Render\Markup objects so the arguments are not passed correctly to the view.

After researching, I've found that I can workaround this issue by filtering my arg variables with a php filter that returns a string. For example, using the trim filter works:

{% set arg1 = content.field_arg1|render|trim %}
{% set arg2 = content.field_arg2|render|trim %}

{{ drupal_view('my_view', 'my_display', arg1, arg2) }}

If possible, I would like to build a render array that doesn't require any fancy footwork in the template to pass the values to the drupal_view() function. I.e. a render array where I can pass content.field_arg1 or content.field_arg|render directly as the arguments. Is this possible?

Render arrays I've tried that did not work:

$inline_template = [
  '#type' => 'inline_template',
  '#template' => '{{ contextual_filter_string }}', // also tried with |trim filter here
  '#context' => [
    'contextual_filter_string' => '1+2+3',
  ],
];

$markup = [
  '#markup' => '1+2+3',
];

$plain_text = [
  '#plain_text' => '1+2+3',
];

Note: not shown in the above render arrays is cache metadata (tags) for the entities referenced in the field.

Yes, there are alternate ways to get these field values and pass them to drupal_view(), however that's not what this question is about. I would like to use a Field Formatter to return a render array (I think I have to return a render array) that results in a string ready to be passed directly to drupal_view(). This allows me to configure the format of the argument in the UI without touching code. I suspect this is impossible.

Some alternative approaches that I've considered:

  • I could preprocess my paragraph and add these filter strings to the $variables array.
  • I could build my own twig function to generate these contextual filter strings.
  • I could get the values directly from the entity in my template, e.g. paragraph.field_arg1.target_id as Les Lim answered, but this doesn't work cleanly for multi-value fields.

Edit: It seems that this is impossible so accepting Les Lim's answer as the best alternative.

4uk4 avatar
cn flag
For PHP see https://drupal.stackexchange.com/questions/207699/how-to-get-an-array-of-referenced-entity-ids-from-an-entity-reference-field. If you use the current Drupal Twig version 2.14 this is also possible in Twig. See https://twig.symfony.com/doc/2.x/filters/column.html
Les Lim avatar
us flag
I edited my answer, and then refreshed to see your addendum to the question at the bottom. I'm not sure I have a direct answer for you; I think what you're discovering is that the field formatter system wasn't designed to be used in the way that you're trying to use it. It assumes that it should be returning filtered/escaped HTML markup, not raw unescaped strings that you'd want to use as a View argument.
sonfd avatar
in flag
@LesLim - Yes, thank you, that's my impression too. I really wanted the formatter to work cleanly because it would make it so easy to configure and reuse without much PHP or Twig knowledge. Your edits are helpful, even though it ultimately does not answer my question.
Les Lim avatar
us flag
@4k4 Thanks for the tip about Twig's column filter! I edited my answer again.
Score:7
us flag

Original response, assuming you only need a single value from each field:

Instead of using the rendered field output that you get with items in the {{ content }} array, you should probably use raw values directly from the entity. Raw values are much more likely to be what your Views arguments are looking for in the first place.

In Drupal 8/9, if the raw entity is available to the Twig template (and it is for Paragraph templates), you can use Entity API notation to get exact raw values from your entity:

{% set arg1 = paragraph.field_arg1.target_id %}
{% set arg2 = paragraph.field_arg2.target_id %}

Addendum, for needing multiple raw field values joined together

After re-reading the question, I realize you need all the raw values from a field joined together with a string. The contrib module Twig Field Value gives you handy generic tools for exactly this purpose. In this case, you would use its field_raw filter:

{% set arg1 = content.field_arg1|field_raw('target_id')|safe_join('+') %}
{% set arg2 = content.field_arg2|field_raw('target_id')|safe_join(',') %}

Edit: Join multiple raw field values without Twig Field Value (thanks 4k4 for the tip)

You don't actually need Twig Field Value to get the target_id values from a multiple-value field - you can extract multiple values from a raw entity field using Twig's built-in column filter.

{% set arg1 = paragraph.field_arg1|column('target_id')|safe_join('+') %}
{% set arg2 = paragraph.field_arg2|column('target_id')|safe_join(',') %}
sonfd avatar
in flag
Throw a `|default('all')` at the end and you can even get `all` as a value if the field is empty.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.