Accessing the referencing / parent element in twig (paragraphs) - drupal-modules

I have an entity reference field inside a (parent-)paragraph, which references multiple child-paragraphs.
Is it possible to access field values of the referencing paragraph in the children's (referenced paragraph's) twig templates?
Actually I'm just trying to count the total referenced items within one of the referenced item's twig templates itself. So I want to count its siblings + 1, if you want.
I'm aware of the fact that I could preprocess this in a module, but I would like to know if this is possible in twig.

In twig:
{% set paragraph_parent = paragraph.getParentEntity() %}
{% set width = paragraph_parent.field_width.0.value %}
<div class="{{ width }}">{{ content.field_images }}</div>

Since there is no response to this question I have to assume that this is not possible in Twig, but wanted to quick share the mentioned workaround via module...
getParentEntity()
is your friend.
A short example for making the referenced element's count available...
/* implements hook_preprocess_paragraph() (paragraph type product_teaser) */
function mymodule_preprocess_paragraph__product_teaser(&$variables) {
/* makes paragraph siblings count (+ 1/self) available to template */
$siblings_total = 1;
$paragraph = $variables['paragraph'];
$parent_paragraph = $paragraph->getParentEntity();
if ( ( isset($parent_paragraph) ) && ( $parent_paragraph->hasField('field_paragraph_reference') ) ) {
$field_content = $parent_paragraph->get('field_paragraph_reference')->getValue();
if ( isset($field_content[0]['target_id']) ) {
$siblings_total = count($field_content);
}
}
$variables['mymodule_theming_sources']['siblings_total'] = $siblings_total;
}

Another method using twig with 2 simple lines. It works fine.
{% set parent = paragraph._referringItem.parent.parent.entity %}
{{ parent.title.value }}

Related

Twig multi dimensional array - Can't access values

I have a multidimensional array, but for some reason my twig is not responding with the array values.
Below is my twig Dump
array (size=2)
0 =>
object(App\Models\Entities\Strategy\CriticalSuccessFactor)[5]
private int 'csfId' => int 26
private iterable 'kpis' =>
array (size=1)
0 =>
object(App\Models\Entities\Strategy\KeyPerformanceIndicator)[10]
...
1 =>
object(App\Models\Entities\Strategy\CriticalSuccessFactor)[11]
private int 'csfId' => int 27
private iterable 'kpis' =>
array (size=1)
0 =>
object(App\Models\Entities\Strategy\KeyPerformanceIndicator)[12]
I did find this link, but it did not answer my question.
Multidimensional Array in Twig
Below is a respresentation of the array data I am working with
csfs[
private int 'csfId' => int 26
'kpis' => [
private int 'kpiId' => int 42
'objectives' => [
private int 'objectivesId' => int 40
]
]
]
when I am outputting the variable with twig I get nothing.
Here's my Twig:
{% for csf in csfs %}
{% for kpi in csf.kpis %}
<p> kpi ID : {{ kpi.kpiId }}</p>
{% endfor %}
{% endfor %}
{{ csf.csfId }} works. It prints the ID.
I can get the first array values no problem. But I cannot access kpis array
As seen in the documentation from twig,
For convenience’s sake foo.bar does the following things on the PHP
layer:
check if foo is an array and bar a valid element;
if not, and if foo is an object, check that bar is a valid property;
if not, and if foo is an object, check that bar is a valid method (even if bar is the constructor - use __construct() instead);
if not, and if foo is an object, check that getBar is a valid method;
if not, and if foo is an object, check that isBar is a valid method;
if not, and if foo is an object, check that hasBar is a valid method;
if not, return a null value.
Twig also supports a specific syntax for accessing items on PHP
arrays, foo['bar']:
check if foo is an array and bar a valid element;
if not, return a null value.
So you have two options here:
Adjust the model CriticalSuccessFactor
Either you could rename the method getKeyPerformanceIndicators to getKpis or add a method getKpis which just references getKeyPerformanceIndicators
public function getKpis() {
return $this->getKeyPerformanceIndicators();
}
Adjust your template and call the method getKeyPerformanceIndicatorsdirectly rather than let twig magically decide the method
{% for kpi in csf.getKeyPerformanceIndicators%}
...
{% endfor %}

How to use if condition in for loop ? Jinja

I have two list and one variable returned from the Flask page to my HTML page.
a = [11121,11122,11123,11124,11130]
b = [11126,11127,11128,11129,11130]
var = 11121
In python code :
if var in a:
print("comes in a")
elif var in b:
print("comes in b")
else:
print("comes in both")
But I figured out that we need to iterate list first in jinja. (may be I'am wrong)
Now I want to check if var either comes in a, or in b or in both in jinja2.
If I understood you correctly, you don't need for loop for it. You can check it with in tester. Like this:
{{ "comes from a" if var in a }}
{{ "comes from b" if var in b }}
if var in a (or if var in b) check the existence of var in a (or b) arrays of values. It's a form of expression so you can freely use it in the expression context. For instance, you can assign result of checking to the variable:
{% set comes_from_a = var in a %}
{% set comes_from_b = var in b %}

Twig: Determine whether a menu item has a translation

In my Drupal 8 setup, I have two languages configured (German: default, English). Not all pages have a translation into English, but they show up in the navigation.
I'd like to highlight those menu items that link to pages that don't have a translation in the currently selected language.
So how can I do this in Twig? When I dump the menu item, I see an object of class MenuLinkContent that has a field entity which might contain the answer:
object(Drupal\menu_link_content\Plugin\Menu\MenuLinkContent)[31277]
[...]
protected 'entity' =>
object(Drupal\menu_link_content\Entity\MenuLinkContent)[31407]
[...]
protected 'translations' =>
array (size=2)
'x-default' =>
array (size=2)
...
'en' =>
array (size=2)
...
[...]
But I don't seem to be able to actually read those values.
It is easy, first, you have to add your current language to any variable by yourtheme_preprocess
yourtheme_preprocess(&$vars, $hook)
{
$language = \Drupal::languageManager()->getCurrentLanguage()->getId();
$vars['langcode'] = $language;
}
and now in your twig template, you have to check your entity if has a translation,
{% if node.hasTranslation(langcode) %}
{% set node = node.getTranslation(langcode) %}
{% endif %}

Set a value on a multi level array

dump(item.url.options.query)
returns: array (size=0)
{% set item = item|merge({'ref': 'xyz'}) %}
returns: no error, item is now set
{% set item.url.options.query = item.url.options.query|merge({'ref': 'xyz'}) %}
errors: Twig_Error_Syntax: Unexpected token 'punctuation'; of value '.'
How do I set the array index item.url.options.query?
You have a deeply nested array so you need to use the merge filter many times:
{% set item = item|merge({
url: item.urls|merge({
options: item.url.options|merge({
query: item.url.options.query|merge({
ref: 'xyz'
})
})
})
}) %}
If the item variable or some of the array items are objects instead of arrays, you might get an error (because the merge filter only works with arrays and Traversable objects), or the objects might get converted into arrays. If that's the case, you might want to take a look at the question DarkBee linked to.

Django-Haystack - How to use Haystack with django-comments?

I'm struggling with Django-Haystack.
I need to do an Index that have Articles and Comment articles. My doubt is how can I put in a document based index the Articles and the Comments.
How can I search for keywords in the comments and in the articles and output the article with that keywords(article comments, article)?
It is possible?
Best Regards,
The first thing to do is forget the notion that a SearchIndex must correspond exactly to a model. It's only sourced from one.
The simplest way to do this would be to add the comments to the indexed document using a template. This presume your Article model as a title field:
class ArticleIndex(SearchIndex, indexes.Indexable):
text = CharField(document=True, use_template=True)
title = CharField(model_attr='title')
def get_model(self):
return Article
Note the keyword argument use_template is set to true. The default value for this is search/indexes/{app_label}/{model_name}_{field_name}.txt. In that template just output the content you want to index. E.g.
{{ object.title|safe }}
{{ object.body|safe }}
{% for comment in object.comments.all %}
{{ comment|safe }}
{% endfor %}
While I'm afraid the specific reverse relation name here is probably wrong, that's the gist of what you want to do. Again, this is a simple way of accomplishing what you've specifically stated.
This is what worked for me:
In your models.py, presuming comments are attached to an Article, you want a method that returns comments attached to it (there is no easy way to do this):
class Article:
def comments(self):
ids = [self.id]
ctype = ContentType.objects.get_for_model(Article)
comments = Comment.objects.filter(content_type=ctype,
object_pk__in=ids,
is_removed=False)
return comments
In your search_indexes.py, make sure the ArticleIndex has use_template=True:
from django.contrib.contenttypes.models import ContentType
from django.contrib.comments.models import Comment
class ArticleIndex(SearchIndex):
text = CharField(use_template=True)
In your index template, e.g. templates/search/indexes/article_text.txt:
{% for comment in object.comments.all %}
{{ comment }}
{% endfor %}
Now, the only remaining problem is to update that specific index object when a comment is added or removed. Here we use signals:
In your models.py:
from django.dispatch import receiver
from haystack import site
from django.contrib.comments.signals import (comment_was_posted,
comment_was_flagged)
#receiver(comment_was_posted)
def comment_posted(sender, **kwargs):
site.get_index(Article).update_object(kwargs['comment'].content_object)
#receiver(comment_was_flagged)
def comment_flagged(sender, **kwargs):
site.get_index(Article).update_object(kwargs['comment'].content_object)

Resources