How do I access a member in Twig determined by a variable?

13,196

Solution 1

I wrote my own twig extension to do this. You would use it in the way that I wanted:

{% set keyVariable = 'propertyName' %}
{{ obj.access(keyVariable) }}
{# the above prints $obj->propertyName #}

Here is it:

// filename: Acme/MainBundle/Extension/AccessTwigExtension.php
namespace Acme\MainBundle\Extension;

class AccessTwigExtension extends \Twig_Extension
{
    public function getFilters()
    {
        return array(
            'access' => new \Twig_Filter_Method($this, 'accessFilter'),
        );
    }

    public function getName()
    {
        return 'access_twig_extension';
    }

    // Description:
    // Dynamically retrieve the $key of the $obj, in the same order as
    // $obj.$key would have done.
    // Reference:
    // http://twig.sensiolabs.org/doc/templates.html
    public function accessFilter($obj, $key)
    {
        if (is_array($obj)) {
            if (array_key_exists($key, $obj)) {
                return $obj[$key];
            }
        } elseif (is_object($obj)) {
            $reflect = new \ReflectionClass($obj);
            if (property_exists($obj, $key) && $reflect->getProperty($key)->isPublic()) {
                return $obj->$key;
            }
            if (method_exists($obj, $key) && $reflect->getMethod($key)->isPublic()) {
                return $obj->$key();
            }
            $newKey = 'get' . ucfirst($key);
            if (method_exists($obj, $newKey) && $reflect->getMethod($newKey)->isPublic()) {
                return $obj->$newKey();
            }
            $newKey = 'is' . ucfirst($key);
            if (method_exists($obj, $newKey) && $reflect->getMethod($newKey)->isPublic()) {
                return $obj->$newKey();
            }
        }
        return null;
    }
}

To use it in my program, I also had to add a few lines to my dependency injection:

//filename: Acme/MainBundle/DependencyInjection/AcmeMainInjection.php
// other stuff is here....
public function load(array $configs, ContainerBuilder $container)
{
    // other stuff here...
    $definition = new Definition('Lad\MainBundle\Extension\AccessTwigExtension');
    $definition->addTag('twig.extension');
    $container->setDefinition('access_twig_extension', $definition);
    // other stuff here...

Solution 2

Short answer: not directly / natively possible ... yet.

Apparently they added a new function to Twig 1.2 called attribute() which addresses exactly that need.

But as up to this day you can only download Twig 1.1.2; so 1.2 is probably not shipped with SF2 - though I cannot find a version number. (1.2 is available now!)

I tried to solve that with different tricks, but to no avail; 1.2 will fix it.

New in version 1.2: The attribute function was added in Twig 1.2.

attribute can be used to access a “dynamic” attribute of a variable:

{{ attribute(object, method) }}

{{ attribute(object, method,arguments) }}

{{ attribute(array, item) }}


But what you can do though is add a method to your class that takes care of whatever you need. something like that:

php:

class C
{
    public $a = 1;
    public $b = 2;

    public function getValueForKey($k)
    {
        return $this->$k;
    }
}

[ providing an instance of C to the template as 'obj' ]

twig:

{% set x = "a" %}
{{ obj.getValueForKey(x) }}

will output '1'

Solution 3

Use brackets syntax: bldg[key]

Share:
13,196

Related videos on Youtube

Robert Martin
Author by

Robert Martin

Latte art is much, much more difficult than even I imagined.

Updated on June 04, 2022

Comments

  • Robert Martin
    Robert Martin almost 2 years

    I want to do the following code:

    {% set rooms = [] %}
    {% set opts = {
        'hasStudio': 'Studio',
        'has1Bed': '1 BR',
        'has2Bed': '2 BR',
        'has3Bed': '3 BR',
        'has4BedPlus': '4 BR+'
    }
    %}
    {% for key, val in opts %}
        {% if bldg.{key} is none %} {# PROBLEM HERE.. HOW TO FIND THIS MEMBER!? #}
          {{ val }}?
        {% elseif bldg.{key} %}
          {{ val }}
        {% else %}
          No {{ val }}
        {% endif %}
    {% endfor %}
    

    How do I call the member properties of bldg that are named by the value of key? I want to get the values of

     bldg.hasStudio
     bldg.has1Bed
     bldg.has2Bed
     etc....
    
  • Robert Martin
    Robert Martin over 12 years
    Thanks for the info. My problem with this solution is that I didn't want to muddy my Entity classes with getters that are only there because twig needs them...
  • Ajay Patel
    Ajay Patel over 10 years
    attribute feature in twig 1.2 is awesome