Symfony2 display associative array in twig - 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 %}

Related

Shopware 6 - Find seo Url of variant on product detail page

I am trying to lookup the seoUrl of variants displayed on a product detail page - in the configurator.html.twig file. I have the option Id and have tried passing it as productId for the seoUrl function - but it doesn't return the correct seoUrl.
Searching for a solution, I found this question: Show all variations on the product detail page in Shopware 6 - which also lacks an answer.
But it hinted that you should add the data using a Subscriber - is that really necessary?
You may decorate the ProductDetailRoute, fetch the parent and with its children association and iterate them in the storefront template.
<service id="MyPlugin\Core\Content\Product\SalesChannel\Detail\ProductDetailRouteDecorator" public="true" decorates="Shopware\Core\Content\Product\SalesChannel\Detail\ProductDetailRoute">
<argument type="service" id="MyPlugin\Core\Content\Product\SalesChannel\Detail\ProductDetailRouteDecorator.inner"/>
<argument type="service" id="sales_channel.product.repository"/>
</service>
class ProductDetailRouteDecorator extends AbstractProductDetailRoute
{
private SalesChannelRepositoryInterface $productRepository;
private AbstractProductDetailRoute $decorated;
public function __construct(
AbstractProductDetailRoute $decorated,
SalesChannelRepositoryInterface $productRepository
) {
$this->decorated = $decorated;
$this->productRepository = $productRepository;
}
public function getDecorated(): AbstractProductDetailRoute
{
return $this->decorated;
}
public function load(string $productId, Request $request, SalesChannelContext $context, Criteria $criteria): ProductDetailRouteResponse
{
$response = $this->getDecorated()->load($productId, $request, $context, $criteria);
$product = $response->getProduct();
if (!$product->getParentId()) {
return $response;
}
$criteria = new Criteria([$product->getParentId()]);
$criteria->addAssociation('children');
$parent = $this->productRepository->search($criteria, $context)->first();
$product->setParent($parent);
return new ProductDetailRouteResponse($product, $response->getConfigurator());
}
}
{% if page.product.parent.children is defined %}
{% for child in page.product.parent.children %}
{{ seoUrl('frontend.detail.page', { productId: child.id }) }}
{% endfor %}
<br>
{% endif %}
Sample output:
http://localhost/Intelligent-Marble-Ultra-Beef/0491895660e94e32938022263595f861
http://localhost/Intelligent-Marble-Ultra-Beef/7c9f91d9051e40e0ba13d0e885e98d83
http://localhost/Intelligent-Marble-Ultra-Beef/f25c641abee446df82e1227cf200186c
Variation on this solution
A little more complex but with a lesser performance impact:
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('parentId', $product->getParentId()));
$ids = $this->productRepository->searchIds($criteria, $context)->getIds();
$product->addExtension('childrenIds', new ArrayStruct($ids));
{% if page.product.extensions.childrenIds is defined %}
{% for childId in page.product.extensions.childrenIds.all() %}
{{ seoUrl('frontend.detail.page', { productId: childId }) }}
<br>
{% endfor %}
{% endif %}
Yes, the best solution would be to fetch all variants based on the parentId in a subscriber.
This is necessary because you want to have more data then just the productId. You should also consider this information as necessary:
minPurchase
calculatedMaxPurchase
purchaseSteps
And optional:
packUnit
packUnitPlural
Without the necessary product information the end user will possibly encounter errors when adding a product with a invalid quantity to the cart.

Drupal 8 - Twig template won't get variables values

I'm trying to make a twig template to get a variable from a custom block, when I do a {{ dumb() }} it shows me the variables and their values but when I call for the variable it won't show it, even when i call the variable with dumb {{ dumb(title) }} it tells me is NULL. Could anyone help me understand what is the mistake?
Block: onyx_experiencia.php
/**
* Provides a 'Test' Block.
*
* #Block(
* id = "onyx_experiencia",
* admin_label = #Translation("Servicios OnyxGroup"),
* category = #Translation("Servicios OnyxGroup"),
* )
*/
class onyx_experiencia extends BlockBase implements BlockPluginInterface {
/**
* {#inheritdoc}
*/
public function build() {
$title = 'TestTitle34';
$desc = 'Test text 24';
$test_array = array(
'#title' => $title,
'#description' => $desc
);
return $test_array;
}
block.module: onyx_experiencia.module
<?php
/**
* Implements hook_theme().
*/
function onyx_experiencia_theme($existing, $type, $theme, $path) {
return array(
'block__serviciosonyxgroup' => array(
'template' => 'block--serviciosonyxgroup',
'render element' => 'elements',
'variables' => array(
'title' => 'TitleTest',
'description' => 'DescriptionTest'
),
),
);
}
Twig File: block--serviciosonyxgroup.html.twig
{#
/**
* #file
* Profile for onyx_experiencia block.
*/
#}
<h3>Featured Events</h3>
<p>Test: {{ title }} </p>
<p>Test: {{ description }} </p>
<ol>
{% for key, value in _context %}
<li>{{ key }}</li>
{% endfor %}
</ol>
{{ dump(content) }}
Result: This is the result i get
UPDATE Different way still not working
As seen on your screenshot:
The variables live in the variable content, not in _context
Your current template code assumes they in live _context though as they aren't prefixed with anything.
{{ title }} equals <?= isset($_context['title']) ? $_context['title'] : null; ?>
So u'd need to change the template to something like
<h3>Featured Events</h3>
<p>Test: {{ content['#title'] }} </p>
<p>Test: {{ content['#description'] }} </p>

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);

Get length of array with condition on attribute

I'd like if is it possibel to get length of array with condition on attribute without create another query in the controller.
public function pageAction()
{
$em = $this->getDoctrine()->getManager();
$pages = $em->getRepository('AppBundle:Page')->findAll();
return $this->render(':Frontend/includes:menu.html.twig', array(
'pages' => $pages
));
}
in the view
//number of all pages
{{ pages|length }} // output 15 (ok)
now is it possible to get the number of pages where page.activate == true from the same result returned in the controller ?
// number of page where page.activate == true
??
This should work hous:
{% set pageCount = 0 %}{# Sets variable #}
{% for p in pages if p.getActivate %}
{% set pageCount = pageCount + 1 %}
{% endfor %}
<p>Activated Pages: {{ pageCount }}
Try it!

Twig: while-workaround

I've already a solution, but just for JavaScript. Unfortunately while-loops do not exist in Twig.
My Twig-target in JavaScript:
var x = 10; // this is an unknown number
var result = x;
while (100 % result !== 0) {
result++;
}
console.log(result);
Any ideas how I do this in Twig?
What's my target: (not important if you already understood)
I want to get the first number after my unknown number, that satisfy the following condition:
100 divided by (the first number) equals a whole number as result.
EDIT: I have no access to PHP nor Twig-core.
You can make a Twig extension like:
namespace Acme\DemoBundle\Twig\Extension;
class NumberExtension extends \Twig_Extension
{
public function nextNumber($x)
{
$result = $x;
while (100 % $result !== 0) {
$result++;
}
return $result;
}
public function getFunctions()
{
return array(
'nextNumber' => new \Twig_Function_Method($this, 'nextNumber'),
);
}
/**
* Returns the name of the extension.
*
* #return string The extension name
*/
public function getName()
{
return 'demo_number';
}
}
And define it in the service.xml of the bundle:
<service id="twig.extension.acme.demo" class="Acme\DemoBundle\Twig\Extension\NumberExtension" >
<tag name="twig.extension" />
</service>
Then use it in the template:
{{ nextNumber(10) }}
UPDATE
A (not so great) approach but that possibly satisfy your needed is to do something like this:
{% set number = 10 %}
{% set max = number+10000 %} {# if you can define a limit #}
{% set result = -1 %}
{% for i in number..max %}
{% if 100 % i == 0 and result < 0 %} {# the exit condition #}
{% set result = i %}
{% endif %}
{% endfor %}
<h1>{{ result }}</h1>
Hope this help
In my case - I had to output an object with similar subobjects - including a templete with predefined values and setting up a normal if-condition worked.
See http://twig.sensiolabs.org/doc/tags/include.html for more infomration.

Resources