How to set a variable name with dynamic variables? - twig

I am trying to set variables with dynamic names. The code I am using is:
{% for i in 0..2 %}
{% set foo~i = 'array'.'~i~'.'getfoo' %}
{% set bar~i = 'array'.'~i~'.'getbar' %}
{% endfor %}
The variables I want are:
foo0
bar0
foo1
bar1
foo2
bar2
But I get this error Unexpected token "operator" of value "~" ("end of statement block" expected)
Also I don't want these variables as array.

Like #DarkBee mentioned, you can't do this in vanilla Twig. But you can create quite a simple extension โ€“ notice that $context needs to be passed by reference:
class MyTwigExtension extends Twig_Extension {
public function getFunctions() {
return [
new Twig_Function('set', [$this, 'set'], ['needs_context' => true]),
];
}
public function set(&$context, $name, $value) {
$context[$name] = $value;
}
}
$twig->addExtension(new MyTwigExtension());
Then in Twig you can do:
{{ dump() }}
{% do set('foo' ~ 1, 'bar') %}
{{ dump() }}
The above will print:
array(0) {
}
array(1) {
["foo1"]=>
string(3) "bar"
}
But note that a for loop has its own context. So if you do this:
{% set foo = 'bar' %}
Before loop:
{{ dump() }}
{% for i in 0..2 %}
{%- do set('foo' ~ i, 'iteration ' ~ i) %}
{%- if loop.last %}
{{- 'Inside loop (last iteration):\n' }}
{{- loop.last ? dump() }}
{% endif %}
{% endfor %}
After loop:
{{ dump() }}
You get this โ€“ notice the _parent array which represents the "parent" context outside of the loop:
Before loop:
array(1) {
["foo"]=>
string(3) "bar"
}
Inside loop (last iteration):
array(9) {
["foo"]=>
string(3) "bar"
["_parent"]=>
array(1) {
["foo"]=>
string(3) "bar"
}
["_seq"]=>
array(3) {
[0]=>
int(0)
[1]=>
int(1)
[2]=>
int(2)
}
["loop"]=>
array(8) {
["parent"]=>
array(1) {
["foo"]=>
string(3) "bar"
}
["index0"]=>
int(2)
["index"]=>
int(3)
["first"]=>
bool(false)
["revindex0"]=>
int(0)
["revindex"]=>
int(1)
["length"]=>
int(3)
["last"]=>
bool(true)
}
["i"]=>
int(2)
["_key"]=>
int(2)
["foo0"]=>
string(11) "iteration 0"
["foo1"]=>
string(11) "iteration 1"
["foo2"]=>
string(11) "iteration 2"
}
After loop:
array(1) {
["foo"]=>
string(3) "bar"
}
You can overcome this limitation in three ways. First is to initialize the variables before the for loop (notice that foo0 is left as null because the loop starts at 1, and that foo3 won't be in the global context because it hasn't been initialized):
{% set foo0 = null %}
{% set foo1 = null %}
{% set foo2 = null %}
{% for i in 1..3 %}
{% do set('foo' ~ i, 'iteration ' ~ i) %}
{% endfor %}
{{ dump() }}
The above will print:
array(3) {
["foo0"]=>
NULL
["foo1"]=>
string(11) "iteration 1"
["foo2"]=>
string(11) "iteration 2"
}
The second way is to modify the extension's set method to check whether $context contains a key _parent:
public function set(&$context, $name, $value) {
$context[$name] = $value;
if (array_key_exists('_parent', $context)) {
$this->set($context['_parent'], $name, $value);
}
}
Then even nested for loops aren't a problem:
{% for i in 1..2 %}
{% for j in 3..4 %}
{% do set('foo' ~ i ~ j, i ~ ' and ' ~ j) %}
{% endfor %}
{% endfor %}
{{ dump() }}
The above will print:
array(4) {
["foo13"]=>
string(7) "1 and 3"
["foo14"]=>
string(7) "1 and 4"
["foo23"]=>
string(7) "2 and 3"
["foo24"]=>
string(7) "2 and 4"
}
The third way is to keep the extension's set method intact and create a new method, e.g. set_global:
class MyTwigExtension extends Twig_Extension {
public function getFunctions() {
return [
new Twig_Function('set', [$this, 'set'], ['needs_context' => true]),
new Twig_Function('set_global', [$this, 'set_global'], ['needs_context' => true]),
];
}
public function set(&$context, $name, $value) {
$context[$name] = $value;
}
public function set_global(&$context, $name, $value) {
$context[$name] = $value;
if (array_key_exists('_parent', $context)) {
return $this->set_global($context['_parent'], $name, $value);
}
}
}
$twig->addExtension(new MyTwigExtension());
Then you can use set to set variables in the current context (e.g. in the context of a for loop) or set_global to set "global" variables (in the context of the file). You can use both methods inside for loops to set new values to already initialized variables.

Related

Terraform Pass Variable (Object) to Templatefile

I have the following variable that I would like to have rendered in a template file. All values โ€‹โ€‹of the variable are optional.
The template itself contains other variables, but these are usually just simple strings.
variable "security_context" {
type = object({
allow_privilege_escalation = optional(bool)
read_only_root_filesystem = optional(bool)
run_as_non_root = optional(bool)
privileged = optional(bool)
capabilities = optional(object({
add = optional(list(string))
drop = optional(list(string))
}))
})
default = null
}
resource "helm_release" "test" {
...
values = [templatefile("${path.module}/values-override.yaml.tpl",
{
...
sec_context = var.security_context
}
)]
}
My template file looks like this:
%{ if main_container_sec_context != null }
securityContext:
%{ if sec_context.allow_privilege_escalation != null }
allowPrivilegeEscalation: ${sec_context.allow_privilege_escalation}
%{ endif }
%{ if sec_context.read_only_root_filesystem != null }
readOnlyRootFilesystem: ${sec_context.read_only_root_filesystem}
%{ endif }
%{ if sec_context.run_as_non_root != null }
runAsNonRoot: ${sec_context.run_as_non_root}
%{ endif }
%{ if sec_context.privileged != null }
privileged: ${sec_context.privileged}
%{ endif }
%{ endif }
For the bool values, this approach works, but it's a lot of code. Can this be reduced by iterating over the values?
What would be the best way to integrate the lists (capabilities: add and drop)?

how to customize dialog depending on value of property

I have a single Content view that returns objects from a Content restdb database that has a field specifying contenttype = news, facts, faq, etc.
I want the content result view to toggle to the correct phrasing depending on the value of the property. e.g.:
message {
switch (this.contenttype) {
case (news)
{
if ($handsFree) {
if (size(this) > 1)
{
template ("Latest headlines")
{speech ("Latest headlines from #{value(this.identifier)}")}
}
else-if (size(this) == 1)
{template ("")
{speech ("#{value(this.title)}. \n #{value(this.text)}")
}}
}
else {
if (size(this) > 1)
{
template ("Latest headlines")
{speech ("Latest headlines from #{value(this.identifier)}")}
}
else-if (size(this) == 1)
{template ("")
{speech ("#{value(this.title)}.")
}}
}
}
}
switch (this.contenttype) {
case (facts)
{
if ($handsFree) { if (size(this) > 1)
{
template ("I found some facts")
{speech ("Random facts from #{value(this.identifier)}")}
}
else-if (size(this) == 1)
{template ("")
{speech ("#{value(this.title)}. \n #{value(this.text)}")
}}
}
else {
if (size(this) > 1)
{
template ("I found some facts")
{speech ("Random facts from #{value(this.identifier)}")}
}
else-if (size(this) == 1)
{template ("")
{speech ("#{value(this.title)}.")
}}
}
}
}
}
However, this doesn't work. It is bringing back the system default dialog, presumably because something is not working about the switch statement. What are some ways to make this work?
The problem with the code here is the test for the value of (this.contenttype). Contenttype is an array so does not evaluate to a property value. The following code does work by simply looking at the value of the property in the first item in the array:
message {
switch (this.contenttype[0]) {
case (news)
{template ("news")}
case ("facts")
{
if (size(this) > 1) {
template ("I haz found facts")
} else {
template ("#{event(this, 'Result')}")
}
}
default {template ("default is answer")
}
This relies on the server correctly returning all items with the same property as intended by the query.

Twig nested foreach loop not showing results

I am building a simple chat system for users within my DB, and I want the user to be able to reply to messages, but to show in chronological order. I cannot get the nested foreach loop to show any results. Here is what i have so far.
Friends Database with both users from my users table, id's 2 and 15.
| id | user_id | friend_id | accepted |
| 1 | 15 | 2 | 1 |
Status Database
| id | user_id | parent_id | body |
| 28 | 15 | NULL | Hello |
| 29 | 2 | 28 | Hi, how are you |
This is my Members Controller with the query builder.
public function members(Request $request, Response $response, $args) {
$token = User::where('id', $this->auth->user()->id)->first();
$order = Order::where('user_id', $this->auth->user()->id)->first();
if(!$order) {
$token = $args['token'];
return $response->withRedirect($this->router->pathFor('membersPaymentPlan', compact('token')));
}
$statuses = Status::notReply()->where(function($query) {
return $query->where('user_id', $this->auth->user()->id)->orWhereIn('user_id', $this->auth->user()->friends()->pluck('id'));
})->orderBy('id', 'DESC')->get();
return $this->view->render($response, 'members/index.php', compact('token', 'statuses'));
}
In my User Model, I have method relationships.
public function statuses() {
return $this->hasMany('Base\Models\Messaging\Status', 'user_id');
}
public function friendsOfMine() {
return $this->belongsToMany('Base\Models\User\User', 'friends', 'user_id', 'friend_id');
}
public function friendOf() {
return $this->belongsToMany('Base\Models\User\User', 'friends', 'friend_id', 'user_id');
}
public function friends() {
return $this->friendsOfMine()->wherePivot('accepted', true)->get()->merge($this->friendOf()->wherePivot('accepted', true)->get());
}
In my Status Model. I have method relationships.
public function user() {
return $this->belongsTo('Base\Models\User\User', 'user_id');
}
public function scopeNotReply($query) {
return $query->whereNull('parent_id');
}
public function replies() {
return $this->hasMany('Base\Models\Messaging\Status', 'parent_id');
}
Here is where the problem is. This is my nested foreach loop within my Twig template.
{% for status in statuses %} // This loop works ok showing all results
{% for reply in status.replies() %} // HERE IS THE PROBLEM - No results
<div class="from-them margin-bottom-10">
<p class="nomargin">{{ reply.body | nl2br }}</p>
</div>
<div class="clearfix"></div>
{% endfor %}
<div class="from-me margin-bottom-10">
<p class="nomargin">{{ status.body | nl2br }}</p>
</div>
<div class="clearfix"></div>
{% endfor %}
Why won't the nested foreach loop show any results from the status.replies() relationship? What do I have wrong here?

Call static function in Twig

I am pretty new to Twig. I have a class called Session and a static function called get I want to return the content of Session::get('user_name'). Is it even possible without modifying anything?
I tried {{ constant('Namespace\\Session::get("user_name")') }} and {{ Session.get('user_name') }} but it doesn't seem to work.
You could use Twig Extension like below:
class CustomExtension extends \Twig_Extension {
public function getFunctions() {
return array(
new \Twig_SimpleFunction('static_call', array($this, 'staticCall')),
);
}
function staticCall($class, $function, $args = array()) {
if (class_exists($class) && method_exists($class, $function)) {
return call_user_func_array(array($class, $function), $args);
}
return null;
}
}
Usage in in twig:
{{ static_call('AppBundle\\Entity\\YourEntity', 'GetSomething', ['var1', 'var2']) }}
I found the answer. I simply added the following code right after new Twig_Enviroment($twig_loader)
$twig = new Twig_Environment($twig_loader);
$twig->addFunction('_call', new Twig_Function_Function(
function($class, $function, $arguments = array())
{
return call_user_func(array($class, $function), $arguments);
})
);
In Twig
{{ _call('Session', 'get', 'user_name')|raw }}

filter a new customfield Phreeze

I am using Phreeze to create an application.
Everything goes right but when I want to filter in a template a custom field (tagtypeName) it doesn't do it.
I have the model in model.js:
model.TagModel = Backbone.Model.extend({
urlRoot: 'api/tag',
idAttribute: 'id',
id: '',
name: '',
type: '',
TagtypeName: '',
baja: '',
defaults: {
'id': null,
'name': '',
'type': '',
'baja': ''
}
});
My criteria is TagCriteria.php:
public function GetFieldFromProp($propname)
{
switch($propname)
{
case 'TagtypeName':
return 'tagtype.id';
case 'Id':
return 'tag.id';
case 'Name':
return 'tag.name';
case 'Type':
return 'tag.type';
case 'Baja':
return 'tag.baja';
default:
return parent::GetFieldFromProp($propname);
//throw new Exception('Unable to locate the database column for the property: ' . $propname);
}
}
My reporter is:
public $Id;
public $Name;
public $Type;
public $Baja;
public $TagtypeName;
/*
* GetCustomQuery returns a fully formed SQL statement. The result columns
* must match with the properties of this reporter object.
*
* #see Reporter::GetCustomQuery
* #param Criteria $criteria
* #return string SQL statement
*/
static function GetCustomQuery($criteria)
{
$sql = "select
`tagtype`.`name` as TagtypeName
,`tag`.`id` as Id
,`tag`.`name` as Name
,`tag`.`type` as Type
,`tag`.`baja` as Baja
from `tag`
inner join `tagtype` on `tagtype`.`id`=`tag`.`type`";
// the criteria can be used or you can write your own custom logic.
// be sure to escape any user input with $criteria->Escape()
$sql .= $criteria->GetWhere();
$sql .= $criteria->GetOrder();
return $sql;
}
the view has:
<script type="text/template" id="tagCollectionTemplate">
<table class="collection table table-bordered table-hover">
<thead>
<tr>
<th id="header_Id">Id<% if (page.orderBy == 'Id') { %> <i class='icon-arrow-<%= page.orderDesc ? 'up' : 'down' %>' /><% } %></th>
<th id="header_Name">Name<% if (page.orderBy == 'Name') { %> <i class='icon-arrow-<%= page.orderDesc ? 'up' : 'down' %>' /><% } %></th>
<!--<th id="header_Type">Type<% if (page.orderBy == 'Type') { %> <i class='icon-arrow-<%= page.orderDesc ? 'up' : 'down' %>' /><% } %></th>-->
<th id="header_Type">Type<% if (page.orderBy == 'TagtypeName') { %> <i class='icon-arrow-<%= page.orderDesc ? 'up' : 'down' %>' /><% } %></th>
<th id="header_Baja">Baja<% if (page.orderBy == 'Baja') { %> <i class='icon-arrow-<%= page.orderDesc ? 'up' : 'down' %>' /><% } %></th>
</tr>
</thead>
<tbody>
<% items.each(function(item) { %>
<tr id="<%= _.escape(item.get('id')) %>">
<td><%= _.escape(item.get('id') || '') %></td>
<td><%= _.escape(item.get('name') || '') %></td>
<!--<td><%= _.escape(item.get('type') || '') %></td>-->
<td><%= _.escape(item.get('tagtypeName') || '') %></td>
<td><%= _.escape(item.get('baja') || '') %></td>
</tr>
<% }); %>
</tbody>
</table>
<%= view.getPaginationHtml(page) %>
</script>
and in my controller I've changed the Tag to my reporter:
$tags = $this->Phreezer->Query('TagReporter',$criteria)->GetDataPage($page, $pagesize);
So when I go to the page I see the table instead of with the type id, it has the name of the type from my custome query as needed
but when I filter, no data is found with the new custom field:
Does anyone know how to handle this problem?
The problem was that I had to change the reporter count and put the inner joins:
static function GetCustomCountQuery($criteria)
{
$sql = "select count(1) as counter from `tag`
inner join `tagtype` on `tagtype`.`id`=`tag`.`type`";
// the criteria can be used or you can write your own custom logic.
// be sure to escape any user input with $criteria->Escape()
$sql .= $criteria->GetWhere();
return $sql;
}
Doing this it works OK for me!

Resources