grabbing attributes for twig from controller - php

I am using Drupal and have twig for html template. I am super new to this project and see that the have a controller with a function that queries a database and then they return that query for the course page attributes, and I see where the attributes are displayed in the respective course.twig.html.
What I don't see is how they are connected and how the twig.html knows where its attributes are coming from. I am trying to add some more attributes to the page and don't see how this project is passing the mysql queries to the twig template.
{#
/**
* #file
* Default theme implementation to display a node.
*
* Available variables:
* - node: Full node entity.
* - id: The node ID.
* - bundle: The type of the node, for example, "page" or "article".
* - authorid: The user ID of the node author.
* - createdtime: Time the node was published formatted in Unix timestamp.
* - changedtime: Time the node was changed formatted in Unix timestamp.
* - label: The title of the node.
* - content: All node items. Use {{ content }} to print them all,
* or print a subset such as {{ content.field_example }}. Use
* {{ content|without('field_example') }} to temporarily suppress the printing
* of a given child element.
* - author_picture: The node author user entity, rendered using the "compact"
* view mode.
* - metadata: Metadata for this node.
* - date: Themed creation date field.
* - author_name: Themed author name field.
* - url: Direct URL of the current node.
* - display_submitted: Whether submission information should be displayed.
* - attributes: HTML attributes for the containing element.
* The attributes.class element may contain one or more of the following
* classes:
* - node: The current template type (also known as a "theming hook").
* - node--type-[type]: The current node type. For example, if the node is an
* "Article" it would result in "node--type-article". Note that the machine
* name will often be in a short form of the human readable label.
* - node--view-mode-[view_mode]: The View Mode of the node; for example, a
* teaser would result in: "node--view-mode-teaser", and
* full: "node--view-mode-full".
* The following are controlled through the node publishing options.
* - node--promoted: Appears on nodes promoted to the front page.
* - node--sticky: Appears on nodes ordered above other non-sticky nodes in
* teaser listings.
* - node--unpublished: Appears on unpublished nodes visible only to site
* admins.
* - title_attributes: Same as attributes, except applied to the main title
* tag that appears in the template.
* - content_attributes: Same as attributes, except applied to the main
* content tag that appears in the template.
* - author_attributes: Same as attributes, except applied to the author of
* the node tag that appears in the template.
* - title_prefix: Additional output populated by modules, intended to be
* displayed in front of the main title tag that appears in the template.
* - title_suffix: Additional output populated by modules, intended to be
* displayed after the main title tag that appears in the template.
* - view_mode: View mode; for example, "teaser" or "full".
* - teaser: Flag for the teaser state. Will be true if view_mode is 'teaser'.
* - page: Flag for the full page state. Will be true if view_mode is 'full'.
* - readmore: Flag for more state. Will be true if the teaser content of the
* node cannot hold the main body content.
* - logged_in: Flag for authenticated user status. Will be true when the
* current user is a logged-in member.
* - is_admin: Flag for admin user status. Will be true when the current user
* is an administrator.
*
* #see template_preprocess_node()
*
* #todo Remove the id attribute (or make it a class), because if that gets
* rendered twice on a page this is invalid CSS for example: two lists
* in different view modes.
*
* #ingroup themeable
*/
#}
{# {{ kint() }} #}
<article id="node-{{ node.id }}" {{ attributes }}>
{{node}}
{{ title_prefix }}
{% if not page %}
<h2{{ title_attributes }}>
{{ label }}
</h2>
{% endif %}
{{ title_suffix }}
{% if node.field_packaging.value == '1' %}
{% set image = content.field_image %}
{% set ce = content.field_tax_credit_hours %}
{% set goal = content.field_goal %}
{% set target_audience = content.field_audience %}
{% set objectives = content.field_objectives %}
{% set accreditation = content.field_accreditation %}
{% set disclosure = content.field_disclosure_statement %}
{# {% set references_old = content.field_references %} #}
{% set references = content.field_references_par %}
{% set appendix = content.field_appendix %}
{% set faculty = content.field_faculty %}
{% set related_courses = content.field_related_courses %}
{# set suggested_courses = content.field_suggested_courses #}
{% set additional = content.field_callout %}
{% set expiration = node.field_expiration.value %}
<div class="course-summary row">
{% if image|render %}
<div class="course-image small-6 small-offset-3 medium-4 medium-offset-0 columns">
{{ image }}
</div>
<div class="small-12 medium-8 columns">
{% else %}
<div class="small-12 columns">
{% endif %}
<div class="course-introduction">
<p>Welcome to <em>{{ label }}</em>.</p>
</div>
{% if not resource %}
{% if ce.0|render %}
<small class="credit-hours inline-label">Credit hours: {{ ce.0 }} CE</small>
{% endif %}
{% endif %}
<div class="enroll">
{% if signIn == "yes" %}
{% if regis == true %}
Go
{% else %}
Enroll
{% endif %}
{% else %}
Enroll
{% endif %}
</div>
</div>
</div>
{% if goal|render or objectives|render %}
<ul class="tabs some-tabs" data-responsive-accordion-tabs="tabs small-accordion medium-tabs" data-allow-all-closed="true" data-multi-expand="true" id="course-tabs">
<li class="tabs-title is-active">Overview</li>
{% if appendix|render %}
<li class="tabs-title">Appendix</li>
{% endif %}
{% if references|render %}
<li class="tabs-title">References</li>
{% endif %}
{% if faculty|render %}
<li class="tabs-title">Faculty</li>
{% endif %}
</ul>
<div class="tabs-content" data-tabs-content="course-tabs">
<div id="overview" class="tabs-panel is-active course-overview">
{{ additional|render ? additional }}
{% if goal|render %}
<h2 class="field-label">{{ node.field_goal.fielddefinition.label }}</h2>
{{ goal }}
{% endif %}
<h2 class="field-label">{{ node.field_audience.fielddefinition.label }}</h2>
{% if target_audience|render %}
{{ target_audience }}
{% else %}
<p>interested health-care professionals.</p>
{% endif %}
{% if objectives|render %}
<h2 class="field-label">{{ node.field_objectives.fielddefinition.label }}</h2>
<p>After completing the activities of this module, you will be able to:</p>
{{ objectives }}
{% endif %}
{% if expiration|render %}
<p>Please note this module expires on <strong>{{ expiration|date('n/j/Y') }}</strong>.</p>
{% endif %}
{% if accreditation|render or disclosure|render %}
<div class="course-supplements">
{% if accreditation|render %}
<h2 class="supplement-title">Accreditation Statement</h2>
<div id="accreditation" class="callout callout-arrow is-hidden" data-toggler=".is-hidden">
{% for i, value in accreditation %}
{% set acc_item = node.field_accreditation[i].entity %}
{% if acc_item %}
<h3>{{ acc_item.title.value }}</h3>
{% autoescape false %}
{{ acc_item.body.value|replace({'{{ #.## }}': ce.0|escape }) }}
{% endautoescape %}
{% endif %}
{% endfor %}
<button class="close-button" data-toggle="accreditation">×</button>
</div>
{% endif %}
{% if disclosure|render %}
<h2 class="supplement-title">Disclosures</h2>
<div id="disclosure" class="callout callout-arrow is-hidden" data-toggler=".is-hidden">
{{ disclosure }}
<button class="close-button" data-toggle="disclosure">×</button>
</div>
{% endif %}
</div>
{% endif %}
</div>
{% if appendix|render %}
<div id="appendix" class="tabs-panel course-appendix">
{% if appendix %}
<ul class="tabs tabs-style-text" data-tabs id="course-appendix-tabs">
{% for i, value in node.field_appendix.value %}
{% set ref_title = node.field_appendix[i].entity.field_title.value %}
{% if ref_title %}
<li class="tabs-title{{ i == 0 ? ' is-active'}}">{{ ref_title }}</li>
{% endif %}
{% endfor %}
</ul>
<div class="tabs-content" data-tabs-content="course-appendix-tabs">
{% for i, value in node.field_appendix.value %}
<div id="{{ node.field_appendix[i].entity.field_title.value|clean_class }}" class="tabs-panel{{ i == 0 ? ' is-active'}}">
{% autoescape false %}
{{ node.field_appendix[i].entity.field_content.value }}
{% endautoescape %}
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endif %}
{% if references|render %}
<div id="references" class="tabs-panel course-references">
{% if references %}
<ul class="tabs tabs-style-text" data-tabs id="course-references-tabs">
{% for i, value in node.field_references_par.value %}
{% set ref_title = node.field_references_par[i].entity.field_title.value %}
{% if ref_title %}
<li class="tabs-title{{ i == 0 ? ' is-active'}}">{{ ref_title }}</li>
{% endif %}
{% endfor %}
</ul>
<div class="tabs-content" data-tabs-content="course-references-tabs">
{% for i, value in node.field_references_par.value %}
<div id="{{node.field_references_par[i].entity.field_title.value|clean_class}}" class="tabs-panel{{ i == 0 ? ' is-active'}}">
{% autoescape false %}
{{ node.field_references_par[i].entity.field_content.value }}
{% endautoescape %}
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endif %}
{% if faculty|render %}
<div id="faculty" class="tabs-panel course-faculty">
<div class="sections">
{{ faculty }}
</div>
</div>
{% endif %}
</div>
{% endif %}
{% if related_courses|render %}
{% set ancillary_1 = node.field_related_courses.fielddefinition.label %}
{% set ancillary_2 = 'Guidance' %}
{# set ancillary_2 = node.field_suggested_courses.fielddefinition.label #}
<ul class="sections sections-border accordion mobile-accordion stacked-tabs" data-accordion data-allow-all-closed="true" data-multi-expand="true">
<li class="section accordion-item" data-accordion-item>
{{ ancillary_1 }}
{% if related_courses|render %}
<div class="tiles mobile-accordion-content" data-tab-content>
<h2 class="field-label section-title hide-for-small-only">{{ ancillary_1 }}</h2>
<div class="list-items list-format-3">
{{ related_courses }}
</div>
</div>
{% endif %}
</li>
<li class="section accordion-item" data-accordion-item>
{{ ancillary_2 }}
<div class="tiles mobile-accordion-content" data-tab-content>
<h2 class="field-label section-title hide-for-small-only">{{ ancillary_2 }}</h2>
<div class="list-items list-format-1 list-type-guidance">
{{ drupal_view('content_feed', 'guidance') }}
</div>
</div>
</li>
</ul>
{{ drupal_view('content_feed', 'guidance_modal') }}
{% endif %}
{% else %}
{{ content.body }}
{% endif %}
</article>

You should search for preprocess hooks (function yourmodule_preprocess...). If you are using drupal8 you can set a new variable like this:
$vars['myvar'] = 'test';
In twig, can be called like this
{{ myvar }}
Important: it will not work in every function. I use to work it with preprocess functions, but thanks to #leymannx, we know it works with other hooks too:
"Not only preprocess hooks can pass variables to templates. Implement
hook_theme and then in your controler theme the output and pass
variables along to a template"

When you render a twig template in the controller, you can also pass some variables as data. If you want to show that data in the twig template, you call them by their name inside twig functions and language constructs.
That's it in general.
I can not help you more because your question is very generic.

Related

get first item in loop

I am looping through an array and would like to add a class if it is the first item.
I have tried
{% if field|first %}
{% if field.first %}
{% if field.first() %}
{% if loop|first %}
{% if loop.first %}
{% if loop.first() %}
none work, please help. (the reason I don't use css pseudo first is that this is a grid layout and I have am doing responsive layout things with it)
{% for field in row.fields %}
<div class="c-product-table__item {% if field|first %}{{ first_item }}{% endif %} ">
<span class="c-product-table__text">{{ field.text }}</span>
</div>
{% endfor %}
I don't know why twig filters are not working, I solved it another way. I set a var and made it blank after first loop
{% set thiscount = 'first_item' %}
{% for field in row.fields %}
<div class="c-product-table__item {{ thiscount }}">
<span class="c-product-table__text">{{ field.text }}</span>
</div>
{% set thiscount = '' %}
{% endfor %}

Render a form ONLY ONCE inside a for loop in a twig template

I have a twig template to display audio options (Auto play and Continuos play) as shown in the screen shot:
Click to see the screen shot of the page
The following is the code in my twig file. {{ audio_options_form}} renders the form with the checkboxes.I want to display the check boxes only once if {{ item.audio }} is true. Please let me know what changes should I make:
<div id="textcontent">
{% set field = data|slice(2) %}
{% set field = field|slice(0, -1) %}
<div class="col-md-12">
<div class = "source-box audio">
{% for item in field %}
{% if item.audio %}
{{ audio_options_form }}
{% if data.play.autoplay == 1 %}
<audio autoplay controls src= "{{item.audio}}" id = "audio-play">
Your browser does not support the
<code>audio</code> element.
</audio>
{% elseif data.play.continuousplay == 1 %}
<audio autoplay controls src= "{{item.audio}}" id = "audio-play" onended="continousPlay('{{data.lastlevel}}')">
Your browser does not support the
<code>audio</code> element.
</audio>
{% else %}
<audio controls src= "{{item.audio}}" id = "audio-play">
Your browser does not support the
<code>audio</code> element.
</audio>
{% endif %}
<div id="source-controls">
{# {% if allow_edit == 1 %}
<a class="edit-text" href="{{ path('heritage_text_manager.editsource', {'sourceid': item.uniqueid}, {'query': {'destination': path('heritage_ui.contentpage', {'textid': textid})}}) }}">{{ 'Edit'|t }}</a>
{% endif %} #}
<a class="more use-ajax" data-toggle="modal" data-dialog-type="modal" href="{{ path('heritage_ui.metadata', {'sourceid': item.uniqueid}) }}">{{ 'More'|t }}</a>
</div>
{% endif %}
{% endfor %}
</div>
Either create a 2nd loop with a flag or use the filter filter.
{% set show_audio_form = false %}
{% for item in field %}
{% if item.audio %}
{% set show_audio_form = true %}
{% endif %}
{% endfor %}
{% if show_audio_form %}{{ audio_options_form }}{% endif %}
{% for item in field %}
... display audio ...
{% endfor %}
{% if items|filter(v => v.audio|default)| length %}
{{ audio_options_form }}
{% endif %}
{% for item in items %}
... display audio ...
{% endfor %}
demo

Twig show n keys of array or all

I have an editor with a set of buttons, and I want to display only a set of buttons based on twig::render variables.
If I include all I want it to display are buttons available, if I include individual button keys I want to display only that ones.
echo TwigLoader::render('#ui/editor.html.twig'['toolbar'=>['all']]);
echo TwigLoader::render('#ui/editor.html.twig'['toolbar'=>['font','size']]);
For the template I'm using the following code:
{% set toolbar_tools = [
{'font':'<select class="ql-font"></select>'},
{'size':'<select class="ql-size"></select>'}]
%}
<div id="button-container">
<span class="ql-formats">
{% for tool, key in toolbar_tools %}
{{ tool.key|raw}}
{% endfor %}
</span>
</div>
I'm getting an empty container.
Is this a good strategy or there are better ways?
Seems you`re looking for something like this:
{% set toolbar_tools = {
'font':'<select class="ql-font"></select>',
'size':'<select class="ql-size"></select>'
}
%}
<div id="button-container">
<span class="ql-formats">
{% if toolbar|length > 0 %}
{% for t in toolbar %}
{% if t == 'all' %}
{# show all options #}
{% for tool in toolbar_tools %}
{{ tool|raw }}
{% endfor %}
{% else %}
{# show defined options #}
{{ attribute(toolbar_tools, t)|raw }}
{% endif %}
<br />
{% endfor %}
{% endif %}
</span>
</div>
Hope you will be fine with that.

Parsing current uri to get the current route params inside a partial. How to avoid?

To render TOC (categories tree) inside the base.twig view I call the render() function passing it the corresponding action url:
{% block sidebar %}
{{ render(url('toc_documents_categories')) }}
{% endblock %}
The matching partial view for the '/toc/documents' action (_toc.documents_categories.twig) is defined as follows:
{% set category_id = (current_uri|split('/'))[4] %}
{% macro recursiveCategory(category, active_category_id) %}
<li>
{% if category.children|length %}
<a><span class="icon icon-plus"></span>{{ category.name_displayed }}</a>
{% else %}
{% set active_class = active_category_id == category.id ? 'active' %}
{% set url = app.url_generator.generate('documents_by_category', {category_id: category.id}) %}
<a href="{{ url }}" class="{{ active_class }}">
{{ category.name_displayed }}
</a>
{% endif %}
{% if category.children|length %}
<ul>
{% for child in category.children %}
{{ _self.recursiveCategory(child, active_category_id) }}
{% endfor %}
</ul>
{% endif %}
</li>
{% endmacro %}
{% if categories %}
<div id="categories">
<ul>
{% for category in categories %}
{{ _self.recursiveCategory(category, category_id) }}
{% endfor %}
</ul>
</div>
{% endif %}
As you can see I'm extracting current category's id by parsing current url. This is preceded by setting the current_uri global:
$app->before(function(Request $request) use ($app) {
$app['twig']->addGlobal('current_uri', $request->getRequestUri());
});
Accessing the route information (global.request.attributes.get('_route')) inside the partial view shows the corresponding subrequest route name and not the actual request route name (master request).
Is there a way to avoid manually parsing the current uri and to get the current request route params inside the partial view?
Here's the solution.
render() issues a subrequest, so you have to use the master request context:
use Symfony\Component\HttpFoundation\Request
$app->get('/documents_categories', function(Request $request) use($app) {
$master_request = $app['request_stack']->getMasterRequest();
$current_route = $master_request->get('_route');
$active_category_id = null;
if($current_route === 'documents_by_category') {
$active_category_id = $master_request->get('_route_params')['category_id'];
}
// ... do things: parse toc tree ...
return $app['twig']->render('_toc.documents.categories.twig', array(
"categories" => $toc_tree,
"active_category_id" => $active_category_id
));
})->bind('toc_documents_categories');
Then inside the partial view you have to only reference the passed active_category_id parameter:
{% if categories %}
<div id="categories">
<ul>
{% for category in categories %}
{{ _self.recursiveCategory(category, active_category_id) }}
{% endfor %}
</ul>
</div>
{% endif %}
Thanks to #keyboardSmasher for 'Why not pass the category_id in the render function' comment. Yet I'm not sure if I did it the way he assumed.

Error in nested form and twig display

I have a problem with errors display in my twig templates.
Here is my twig with one nested form form.pictures :
{{ form_start(form) }}
{% if not form.vars.valid %}
<div class="flash-errors-wrapper">
{{ form_errors(form) }}
<div class="form-errors">{{ form_errors(form.pictures) }}</div>
</div>
{% endif %}
{% for formChild in form.pictures %}
<div class="child">
{% if not formChild.vars.valid %}
<div class="flash-errors-wrapper">
{{ form_errors(form) }}
<div class="form-errors">{{ form_errors(form.picture) }}</div>
<div class="form-errors">{{ form_errors(form.caption) }}</div>
</div>
{% endif %}
{{ form_widget(formChild.picture) }}
{{ form_widget(formChild.caption) }}
</div>
{% endfor %}
{{ form_end(form) }}
After submission, when a child form is non valid, my parent form is not valid too.
Problem is that it display empty div on top like :
<div class="flash-errors-wrapper">
<div class="form-errors"></div>
</div>
I don't want that because css exist on flash-errors-wrapper class so style is applied.
Any ideas?
You certainly need to check if one child form is not valid when you check if the parent form is valid. One way to do it would be (Untested, it may need some adaptation):
{% if not form.vars.valid %}
{% set all_childs_valid = True %}
{% for formChild in form.pictures %}
{% if not formChild.vars.valid %}
{% set all_childs_valid = False %}
{% endif %}
{% endfor %}
{% if all_childs_valid %}
<div class="flash-errors-wrapper">
{{ form_errors(form) }}
<div class="form-errors">{{ form_errors(form.pictures) }}</div>
</div>
{% endif %}
{% endif %}
I usually display form errors just as described in docs:
{% if errors|length > 0 %}
<ul>
{% for error in errors %}
<li>{{ error.message }}</li>
{% endfor %}
</ul>
{% endif %}

Categories