Twig: how to add an object into multi level object - twig

In twig template I have an object with multiple levels
I need to add a new object into the multiple object as the sub object
{%
set data = {
'first': 'First',
'data': {
'val_1': 'val_1'
}
}
%}
this should be added to data val_2: val_2
expected result:
{%
set data = {
'first': 'First',
'data': {
'val_1': 'val_1',
'val_2': 'val_2'
}
}
%}

First, in Twig terminology, that's not an object or a key-value array but a hash (see Twig's documentation of literals).
You can't add an item to a hash e.g. by doing {% set data.second = 'Second' %}. Instead you need to use the merge filter:
{%
set data = data|merge({
second: 'Second',
})
%}
{{ dump(data) }}
{# Prints this:
array(3) {
["first"]=>
string(5) "First"
["data"]=>
array(1) {
["val_1"]=>
string(5) "val_1"
}
["second"]=>
string(6) "Second"
}
#}
So, to add an item to a hash inside a hash, you need to use the merge filter twice:
{%
set data = data|merge({
data: data.data|merge({
val_2: 'val_2',
}),
})
%}
{# Prints this:
array(3) {
["first"]=>
string(5) "First"
["data"]=>
array(1) {
["val_1"]=>
string(5) "val_1"
["val_2"]=>
string(5) "val_2"
}
["second"]=>
string(6) "Second"
}
#}
If you do lots of this kind of variable manipulation in Twig, it might be a sign that some of that code might be better put in a controller or model or whatever.

Related

Grav - Pass JSON data from Plugin to Twig

I want to retrieve a JSON from a webservice and incorporate it into a Twig template.
I went through the docs and I found that I could use this option.
I have followed the steps from the doc and I have prepared this plugin:
/var/www/html/grav/user/plugins/category# ls
category.php category.yaml twig
/var/www/html/grav/user/plugins/category# cat category.yaml
enabled: true
/var/www/html/grav/user/plugins/category# cat category.php
<?php
namespace Grav\Plugin;
use \Grav\Common\Plugin;
class CategoryPlugin extends Plugin
{
public static function getSubscribedEvents()
{
return [
'onTwigExtensions' => ['onTwigExtensions', 0]
];
}
public function onTwigExtensions()
{
require_once(__DIR__ . '/twig/CategoryTwigExtension.php');
$this->grav['twig']->twig->addExtension(new CategoryTwigExtension());
}
}
/var/www/html/grav/user/plugins/category# cat twig/CategoryTwigExtension.php
<?php
namespace Grav\Plugin;
class CategoryTwigExtension extends \Twig_Extension
{
public function getName()
{
return 'CategoryTwigExtension';
}
public function getFunctions()
{
return [
new \Twig_SimpleFunction('get_child_category', [$this, 'getChildCategoryFunction'])
];
}
public function getChildCategoryFunction()
{
$json = file_get_contents('http://localhost:8888/get_child_category/2/es_ES');
$obj = json_decode($json);
return $json;
}
}
I then incorporate the following function invocation in the Twig template:
{{ get_child_category() }}
But:
I can get $json string, but how can I pass the whole JSON data and retrieve individually the fields?
In my case if I use:
<span>{{ get_child_category() }}</span>
in Twig I get the following string:
[{"id": 11, "name": "Racoons"}, {"id": 10, "name": "Cats"}]
How would I access individual records in Twig, including iteration over the JSON array and individual field extraction (id, name) for each record?
Your function is returning an array. You need to iterate through it. Here is an example from the Grav docs.
<ul>
{% for cookie in cookies %}
<li>{{ cookie.flavor }}</li>
{% endfor %}
</ul>
A simple list of names from your example is a simple edit.
<ul>
{% for child in get_child_category() %}
<li>{{ child.name }}</li>
{% endfor %}
</ul>

Twig Excel Bundle autosize

How to set autosize in Twig Excel Bundle ?
https://twigexcelbundle.readthedocs.io/en/latest/
You can set the default autoSize property of a sheet to true via columnDimension:
{% xlssheet 'Worksheet' {
columnDimension: {
'default': {
autoSize: true
}
}
}%}
{# ... #}
{% endxlssheet %}
To define it for a specific column, use the letter of the desired column instead of default, per example the column D:
{% xlssheet 'Worksheet' {
columnDimension: {
'D': {
autoSize: true
}
}
}%}
{# ... #}
{% endxlssheet %}

Can I extract an associative array into variables?

Lets say I have an associative array like so:
{% set settings = { 'foo':'bar', 'cat':'mouse', 'apple':'banana' } %}
To use this data I would do the following:
{{ settings.foo }}
{{ settings.cat }}
{{ settings.apple }}
However, I wondered if there is a way to extract the keys to variables, and the values to values? Essentially the same as the PHP Extract function. So I can just do this instead:
{{ foo }}
{{ cat }}
{{ apple }}
My very amateurish attempt to do this started out like this:
{% for key,val in settings %}
{% set key = val %}
{% endfor %}
But obviously that doesn't work (or I wouldn't be here). Is there another approach I could take?
Thanks,
Mark
As most things in Twig this can be done by extending Twig
ProjectTwigExtension.php
class ProjectTwigExtension extends Twig_Extension {
public function getFunctions() {
return array(
new Twig_SimpleFunction('extract', array($this, 'extract'), ['needs_context' => true, ]),
);
}
public function extract(&$context, $value) {
foreach($value as $k => $v) $context[$k] = $v;
}
public function getName() {
return 'ProjectTwigExtension';
}
}
Register class in Twig
$twig = new Twig_Environment($loader);
$twig->addExtension(new ProjectTwigExtension());
template.twig
{{ extract({'foo': 'bar', }) }}
{{ foo }} {# output : bar #}
(sidenote) Seems you can't do this by using a closure (see example below) because the compiler of Twig passes the variables in an array, thus creating a copy
With closure
$twig->addFunction(new Twig_SimpleFunction('extract', function (&$context, $value) {
foreach($value as $k => $v) $context[$k] = $v;
}, ['needs_context' => true, ]));
Compiled result
echo twig_escape_filter($this->env, call_user_func_array($this->env->getFunction('extract')->getCallable(), array($context, array("foo" => "bar", "foobar" => "foo"))), "html", null, true);

Twig - how to merge two-dimensional hashes so that sub-hash is not overriden

I've encountered this problem. I wanted to merge these two hashes:
{% set additional_context = {
attributes:{
'class': 'post-link',
'data-confirm-text': params.confirm
}
} %}
{% set ajax_context = {
attributes: {
'class': '',
'data-href': target,
}
} %}
Doing this:
{% additional_context = additional_context|merge(ajax_context) %}
would override additiona_context.attributes with ajax_context.attributes.
I'd like to have attributes hashes merged as well, not overriden. I cannot change the name of the sub-hash - it has to be attributes.
I didn't find a neat way to do it. Ideas much appreciated. Thanks

Symfony2 display associative array in twig

I am new to the Symfony2 framework and am trying to parse some XML from the lastfm API and display information to the user. this would be in the format of album title, playcount and album image for each item.
I can display all this information so far to the user but this is not really useful as I intend to add CSS styling to my page. Any suggestions would be appreciated.
This is my Controller
/**
* #Route("/lastfm/albums", name="albums")
* #Template()
*/
public function albumsAction()
{
$albumsclass = new Album();
// pull in artist albums
$albums = simplexml_load_file('http://ws.audioscrobbler.com/2.0/? method=artist.gettopalbums&artist=imagine+dragons&api_key=370f98844440c2ecc8e5f7 c6cea8a7a4');
$rank = $albums->xpath('/lfm/topalbums/album/#rank');
$album_name_array=array();
$album_playcount_array=array();
$album_url_array=array();
$album_image_array=array();
foreach ($rank as $ranks){
foreach ($ranks as $rank_id) {
$album_name = $albums->xpath('/lfm/topalbums/album[#rank="'.$rank_id.'"]/name');
$album_playcount = $albums->xpath('/lfm/topalbums/album[#rank="'.$rank_id.'"]/playcount');
$album_url = $albums->xpath('/lfm/topalbums/album[#rank="'.$rank_id.'"]/url');
$album_image = $albums->xpath('/lfm/topalbums/album[#rank="'.$rank_id.'"]/image[4]');
}
$album_name = implode($album_name);
array_push($album_name_array,$album_name);
$album_playcount = implode($album_playcount);
array_push($album_playcount_array,$album_playcount);
$album_url = implode($album_url);
array_push($album_url_array,$album_url);
$album_image = implode($album_image);
array_push($album_image_array,$album_image);
}
$container=array();
for($i=0; $i<sizeof($album_name_array); $i++) {
array_push($container,$album_name_array[$i],$album_playcount_array[$i],$album_ur l_array[$i],$album_image_array[$i]);
}
//$hello = array('album_name'=>$album_name_array,
// 'album_playcount'=>$album_playcount_array,
// 'album_url'=>$album_url_array,
// 'album_image'=>$album_image_array,);
//array_push($album_name_array,$album_playcount_array);
return $this->render('AcmelastfmBundle:Default:albums.html.twig', array(
// 'pageData' => array(
// 'artistxml' => $artistxml,
'rank' => $rank,
'ranks' => $ranks,
//'rank_id' => $rank_id,
// 'ranks' => $ranks,
'album_name' => $album_name_array,
//'album_playcount' => $album_playcount_array[$i],
'album_url' => $album_url_array,
'album_image' => $album_image_array,
'container' =>$container,
'data' => var_export($container, true),
//
// 'hello' => $hello,
// 'james' => array('album_name' => $albumsclass->getAlbumName()),
// ),
));
}
This is my view
{% extends '::lastfmbase.html.twig' %}
{% block title %}Albums{% endblock %}
{% block body %}
{% for key in container %}
{{key}} <br>
{% endfor %}<br>
{% endblock %}
I am basically trying to convert this code in PHP to symfony2. However I cannot find a way to pass the associative array values to twig as I get an array to string conversion error
<?php
// pull in artist albums
$albums = simplexml_load_file('http://ws.audioscrobbler.com/2.0/? method=artist.gettopalbums&artist=imagine+dragons&api_key=370f98844440c2ecc8e5f7 c6cea8a7a4');
$rank = $albums->xpath('/lfm/topalbums/album/#rank');
foreach ($rank as $ranks){
foreach ($ranks as $rank_id) {
$album_name = $albums->xpath('/lfm/topalbums/album[#rank="'.$rank_id.'"]/name');
$album_playcount = $albums->xpath('/lfm/topalbums/album[#rank="'.$rank_id.'"]/playcount');
$album_url = $albums->xpath('/lfm/topalbums/album[#rank="'.$rank_id.'"]/url');
$album_image = $albums->xpath('/lfm/topalbums/album[#rank="'.$rank_id.'"]/image[4]');
}
$album_name = implode($album_name);
$album_playcount = implode($album_playcount);
$album_url = implode($album_url);
$album_image = implode($album_image);
print_r($rank_id);
?>
<article class="album">
<?php
echo "".$album_name."<br>";
echo $album_playcount." listeners<br>";
echo "<div><img src=\"".$album_image."\" title=\"$album_name\" /></div><br>";
?>
</article>
<?php
}
I am not sure exactly what you are asking.
Do you mean this?
{% for key, value in container %}
{{ key }}: {{ value }}
{% endfor %}

Resources