How to check a multidimensional Twig array for val

2019-04-21 06:29发布

To simply check if an array contains a certain value I would do:

{% if myVar in someOtherArray|keys %}
...
{% endif %}

However, my array is multi-dimensional.

$tasks = array(
    'someKey' => 'someValue',
    ...
    'tags' => array(
        '0' => array(
            'id'   => '20',
            'name' => 'someTag',
        ),
        '1' => array(
            'id'   => '30',
            'name' => 'someOtherTag',
        ),
    ),
);

What i would like is to be able to check if the $tasks['tags'] has tag id 20. I hope I'm not confusing you by using the PHP array format.

标签: arrays twig
6条回答
时光不老,我们不散
2楼-- · 2019-04-21 06:32

Setting Flag in Twig

{% set flag = 0 %}

{% for tag in tasks.tags %}
    {% if tag.id == "20" %}
        {% set flag = 1 %}
    {% endif %}
{% endfor %}

{% if flag == 1 %}
    //do something
{% endif %}

Creating a Custom Filter in PHP

To reduce the code in your templates twig has the opportunity to create custom filters. To achieve a more general functionality you can simply use variable variable names and use the attribute name as another parameter.

PHP

$filter = new Twig_SimpleFilter('inTags', function ($tags, $needle) {
    $match = false;
    foreach($tags as $tag){
        if(in_array($needle, $tag)){
            $match = true;
            break;
        }
    }
    return $match;
});

$twig = new Twig_Environment($loader);
$twig->addFilter($filter);

Twig

{% if tasks.tags|inTags(20) %}
    //do something
{% endif %}
查看更多
萌系小妹纸
3楼-- · 2019-04-21 06:33

this one is more like a multidimensional loop in case it's necessary

   {% for animals in array %}

        {% set dogs = animals.dogs %}

        {% for dog in dogs %}
            {{ dump(dog.type) }}
        {% endfor%}

    {% endfor %}
查看更多
三岁会撩人
4楼-- · 2019-04-21 06:48

{% if myVar is xpath_aware('//tags/*[id=20]') %}

Context

If you are going to do conditions on an arbitrary deep array, why not using the power of xpath? An array is no more than an unserialized XML string after all!

So, the following array:

$data = array(
    'someKey' => 'someValue',
    'foo'     => 'bar',
    'hello'   => array(
        'world' => true,
        'tags'  => array(
            '0' => array(
                'id'   => '20',
                'name' => 'someTag',
            ),
            '1' => array(
                'id'   => '30',
                'name' => 'someOtherTag',
            ),
        ),
    ),
);

Is the equivalent of the XML string (fixed to avoid numeric tags):

<data>
    <someKey>someValue</someKey>
    <foo>bar</foo>
    <hello>
        <world>1</world>
        <tags>
            <item0>
                <id>20</id>
                <name>someTag</name>
            </item0>
            <item1>
                <id>30</id>
                <name>someOtherTag</name>
            </item1>
        </tags>
    </hello>
</data>

And you want to know if your array matches the following xpath expression:

//tags/*[id=20]

Implementation

We create a new twig Test that will convert our array into a SimpleXMLElement object, and use SimpleXMLElement::xpath() to check if a given xpath matches.

$test = new Twig_SimpleTest('xpath_aware', function (array $data, $path) {
    $xml = new SimpleXMLElement('<?xml version="1.0"?><data></data>');
    array_to_xml($data, $xml); // see full implementation below

    return array() !== $xml->xpath($path);
});

We are now able to run the following test in Twig:

{% if myVar is xpath_aware('//tags/*[id=20]') %}

Full executable implementation:

xpath_test.php

<?php

include (__DIR__.'/vendor/autoload.php');

$context = array(
    'myVar' => array(
        'someKey' => 'someValue',
        'foo'     => 'bar',
        'hello'   => array(
            'world' => true,
            'tags'  => array(
                '0' => array(
                    'id'   => '20',
                    'name' => 'someTag',
                ),
                '1' => array(
                    'id'   => '30',
                    'name' => 'someOtherTag',
                ),
            ),
        ),
    ),
);

// http://stackoverflow.com/a/5965940/731138
function array_to_xml($data, &$xml_data)
{
    foreach ($data as $key => $value) {
        if (is_array($value)) {
            if (is_numeric($key)) {
                $key = 'item'.$key; //dealing with <0/>..<n/> issues
            }
            $subnode = $xml_data->addChild($key);
            array_to_xml($value, $subnode);
        } else {
            $xml_data->addChild("$key", htmlspecialchars("$value"));
        }
    }
}

$twig = new Twig_Environment(new Twig_Loader_Array([]));

$test = new Twig_SimpleTest('xpath_aware', function (array $data, $path) {
    $xml = new SimpleXMLElement('<?xml version="1.0"?><data></data>');
    array_to_xml($data, $xml);

    return array() !== $xml->xpath($path);
});

$twig->addTest($test);

$source = <<< EOT
{% if myVar is xpath_aware('//tags/*[id=20]') %}
It matches!
{% endif %}
EOT;

$template = $twig->createTemplate($source);
echo $template->display($context);

To run it

composer require twig/twig
php xpath_test.php
查看更多
孤傲高冷的网名
5楼-- · 2019-04-21 06:49

For an if-statement within a multi-dimensional array in Twig. Check within the for-loop and then the if statement.

Here is the shorthand for this with Twig:

{% for tag in tasks.tags if tag.id == '20' %}      
       here_if_true
{% endfor %}    

---- EDIT ----

FOR ELSE

To do an else. So the else here is if nothing is found in the entire for:

{% for tag in tasks.tags %}    
       here_if_true
{% else %}
       if there was nothing found
{% endfor %}    

FOR-IF ELSE

Making a combination of the if and else is possible, but it is NOT the same as an if else inside the for loop. Because the else is for the for and not for the if.

{% for tag in tasks.tags if tag.name == 'blue' %}      
    This will fire if in the FOR the tag.name that is blue
{% else %}
    This will fire if there were NO tag.name blue were found ENTIRE FOR!
{% endfor %}

LIVE example

FOR-IF ELSE and IF ELSE

{% for tag in tasks.tags if tag.id == 3 %}    
    the id is 3
    {% if tag.name == 'blue' %}
    the id of the tag is 3 and tag.name is blue
    {% else %} 
    the id of the tag is 3 but the tag.name is not blue
    {% endif %}
{% else %}
    there was no tag.id 3 found in the tasks.tags
{% endfor %}

LIVE example

TWIG documentation

查看更多
ゆ 、 Hurt°
6楼-- · 2019-04-21 06:49

I found myself the solution. Didn't expect it to be so simple. Sometimes I guess I just try to make things too complicated.

{% for tag in tasks.tags %}
    {% if tag.id == '20' %}
        This tag has ID 20
    {% endif %}
{% endfor %}

In my opinion this is not the most efficient way but it does the trick for me at the moment.

Edit

Yenne Info tipped me about the following method. It's a bit cleaner. I don't know if it improves performance though.

{% for tag in tasks.tags if tag.id == '20' %}
    Bingo! We've got a match
{% endfor %}
查看更多
女痞
7楼-- · 2019-04-21 06:53

Set a flag and use a loop. Afterwards you can use the flag in if conditions.

{% set flag = 0 %}
{% for tag in tasks.tags %}
    {% if tag.id == "20" %}
        {% set flag = 1 %}
    {% endif %}
{% endfor %}
{{ flag }}
查看更多
登录 后发表回答