Get length of array with condition on attribute - twig

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!

Related

Variable Variable in Twig

I have the following variables in twig:( I can see them with kint)
data.po_user_setting.us_highlight_color1 = '#008080'
data.po_user_setting.us_highlight_color2 = '#00FFFF'
data.po_user_setting.us_highlight_color3 = '#FFFF00'
data.po_user_setting.us_highlight_color4 = '#FF0000'
data.po_user_setting.us_highlight_color5 = '#FF00FF'
and
verse.po_verse_highlight.hl_rating = Returns [1-5]
How can I show the dynamic variable like this? Neither of these lines work:
{{_context['data.po_user_setting.us_highlight_color' ~ verse.po_verse_highlight.hl_rating]}}
{{attribute(_context, 'data.po_user_setting.us_highlight_color' ~ verse.po_verse_highlight.hl_rating)}}
The problem you are facing is the variable you want to access is actually an array.
With your current code, twig is looking for a variable called e.g., data.po_user_setting.us_highlight_color.foo, translated to $_context['data.po_user_setting.us_highlight_color.foo']
To actually access the variable you want, you would need to treat the variable like an array:
{{ {{ _context['data']['po_user_setting']['us_highlight_color'~ verse.po_verse_highlight.hl_rating] }} }}
This is quite long to type every time, so to reduce the typing you could use a macro or extend twig
macro
{% macro get_array_value(context, key) %}
{% set value = null %}
{% for key in key|split('.') %}
{% set value = loop.first ? context[key]|default : value[key]|default %}
{% endfor %}
{{ value }}
{% endmacro %}
As macro's don't have access to the special variable _context you would to pass this every time when you want to call the macro, e.g.
{% import _self as macros %}
{{ macros.get_array_value(_context, 'data.po_user_setting.us_highlight_color' ~ verse.po_verse_highlight.hl_rating) }}
extending twig
<?php
$twig->addFunction(new \Twig\TwigFunction('get_array_value', function ($context, $variable) {
$keys = explode('.', $variable);
if (empty($keys)) return;
$value = $context[array_shift($keys)] ?? [];
foreach($keys as $key) {
$value = $value[$key] ?? [];
}
return !empty($value) ? $value : null;
}, ['needs_context' => true,]));
Then you can call this function like the following in twig
{{ get_array_value('data.po_user_setting.us_highlight_color' ~ verse.po_verse_highlight.hl_rating) }}

How can I search for the search query value from multiple specified fields in Craft CMS?

In Craft CMS I want to search for the search query value for only some fields/ multiple fields - but not all.
For example limiting to the fields title, introduction, cardContent.
I've added a search: property to to my queryEntry object with the value of title and the query string. But I would like to add more fields.
{% set searchQuery = craft.app.request.getParam('q') %}
{# {% set queryEntries = craft.entries({
section: queryFilters
}).search(searchQuery) %} #}
{% set queryEntries = craft.entries({
search: 'title:' ~ searchQuery,
order: 'score'
}) %}
{% if craft.app.request.getParam('q') %}
{% set searchQuery = '"' ~ craft.app.request.getParam('q') ~ '"' %}
{% set queryEntries = craft.entries({
search: 'title:' ~ searchQuery ~ ' OR cardContent:' ~ searchQuery ~ ' OR introduction:' ~ searchQuery ,
order: 'score'
}) %}
{% endif %}
Get the query string
add searchTerms is concatinated in a string using OR and the query
This returns the array of entries matching the queryEntries.search and you can do what you like with this - eg loop over and display results
You can concatenate any number of fields with their value in the variable and then you can simple pass that in the search parameter with entries query. Here is the example code for that.
{% set nameparam = craft.app.request.getParam('data') %}
{% set categoryparam = craft.app.request.getParam('data1') %}
{% set queryString = '' %}
{% if nameparam is defined and nameparam is not empty %}
{% set queryString = queryString ~ 'title:*'~nameparam~'* ' %}
{% endif %}
{% if categoryparam is defined and categoryparam is not empty %}
{% set queryString = queryString ~ 'blogCategory:'~categoryparam~' ' %}
{% endif %}
{% if queryString is defined and queryString is not empty %}
{% set queryParams = {
search: {
query: queryString,
order: 'score'
},
} %}
{% else %}
{% set queryParams = {} %}
{% endif %}
{% set queryEntries = craft.entries(queryParams) %}

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 %}

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.

Check if variable is string or array in Twig

Is it possible to check if given variable is string in Twig ?
Expected solution:
messages.en.yml:
hello:
stranger: Hello stranger !
known: Hello %name% !
Twig template:
{% set title='hello.stranger' %}
{% set title=['hello.known',{'%name%' : 'hsz'}] %}
{% if title is string %}
{{ title|trans }}
{% else %}
{{ title[0]|trans(title[1]) }}
{% endif %}
Is it possible to do it this way ? Or maybe you have better solution ?
Can be done with the test iterable, added in twig1.7, as Wouter J stated in the comment :
{# evaluates to true if the users variable is iterable #}
{% if users is iterable %}
{% for user in users %}
Hello {{ user }}!
{% endfor %}
{% else %}
{# users is probably a string #}
Hello {{ users }}!
{% endif %}
Reference : iterable
Ok, I did it with:
{% if title[0] is not defined %}
{{ title|trans }}
{% else %}
{{ title[0]|trans(title[1]) }}
{% endif %}
Ugly, but works.
I found iterable to not be good enough since other objects can also be iterable, and are clearly different than an array.
Therefore adding a new Twig_SimpleTest to check if an item is_array is much more explicit. You can add this to your app configuration / after twig is bootstrapped.
$isArray= new Twig_SimpleTest('array', function ($value) {
return is_array($value);
});
$twig->addTest($isArray);
Usage becomes very clean:
{% if value is array %}
<!-- handle array -->
{% else %}
<!-- handle non-array -->
{% endif % }
There is no way to check it correctly using code from the box.
It's better to create custom TwigExtension and add custom check (or use code from OptionResolver).
So, as the result, for Twig 3, it will be smth like this
class CoreExtension extends AbstractExtension
{
public function getTests(): array
{
return [
new TwigTest('instanceof', [$this, 'instanceof']),
];
}
public function instanceof($value, string $type): bool
{
return ('null' === $type && null === $value)
|| (\function_exists($func = 'is_'.$type) && $func($value))
|| $value instanceof $type;
}
}
Assuming you know for a fact that a value is always either a string or an array:
{% if value is iterable and value is not string %}
...
{% else %}
...
{% endif %}
This worked good enough for me in a project I was working on. I realize you may need another solution.

Resources