Twig instanceof for inheritance objects

23,093

Solution 1

I share the opinion, that instanceof is nothing that should appear in a template. I use twig-tests for this case

class MyTwigExtension extends TwigExtension
{
    public function getTests ()
    {
        return [
            new \Twig_SimpleTest('birthday', function (Event $event) { return $event instanceof Birthday; }),
            new \Twig_SimpleTest('walking', function (Event $event) { return $event instanceof Walking; })
        ];
    }
}

And in the template

{% if event is birthday %}{# do something #}{% endif %}

Solution 2

An indirect way of accomplishing this would be testing the object for a method, if you know each inherited object has a unique method. Maybe your Birthday class has a getBirthday(), while your Walking class has a getMap()?

{% if yourobject.methodName is defined %}
   //Stuff that should only output when the object in question has the requested method
{% endif %}

Solution 3

Using instanceof in a template is frowned upon from an architectual standpoint. If you find yourself in a position where you "need" it, you have probably uncovered a problem in your architecture. Your getType solution in your case is probably the best. You could still put that into the event base class and read it out the name of the implementing class.

Solution 4

Another solution :

class Event {
    ...
    public function isInstanceOfBirthday() {
        return $this instanceof Birthday;
    }
}

then it will works with any class that inherit from

class Birthday extends Event {}

class Walking extends Event {}

then in your twig :

{{ event.isInstanceOfBirthday ? ... something for Birthday instance ... : ... something for Walking instance ... }}

OR

{% if event.isInstanceOfBirthday %}
    ... do something for Birthday instance ...
{% else %}
    ... do something for Walking instance ...
{% endif %}
Share:
23,093
Alistair Prestidge
Author by

Alistair Prestidge

php programmer/ unix sysadmin

Updated on July 05, 2022

Comments

  • Alistair Prestidge
    Alistair Prestidge almost 2 years

    I am using the following feature from propel http://www.propelorm.org/documentation/09-inheritance.html.

    I am also using Symfony2 and Twig

    I have a class structure using the above feature that looks something like this

    class Event {}
    
    class Birthday extends Event {}
    
    class Walking extends Event {}
    

    now I pass an event object to a twig template and I want to know what type of event it is

    For instance I want to display an image of a cake if its a birthday and I want to display map routes if its walking event.

    I cannot use instanceof in Twig as this feature does not exist. Does anyone now why this does not exist? and is there a way I can replicate this functionality without having to do something like

     public function getType()
    

    in each class, or

     public function isBirthday()
    

    in the event class.

    I found this on github but it is of no use to me. I have commented on their to see if I can get an answer.

    https://github.com/fabpot/Twig/issues/553

  • flu
    flu over 10 years
    There's no magic .method getter added by Twig to each object. So as long as yourobject doesn't actually have a method named "method" you expression will never become true. Instead simply use {% if yourObject.methodName is defined %} where "methodName" is the exact name of the function you're checking for.
  • Benjamin Nolan
    Benjamin Nolan about 10 years
    I think I'm just being dense, but I don't seem to be able to find that TwigTest class anywhere in @fabpot/Twig. Have you defined a custom class that extends \Twig_Test, or is it an alias to another class? \Twig_Test itself is abstract and thus uninstantiatable.
  • Benjamin Nolan
    Benjamin Nolan about 10 years
    Found it. I'm guessing the class was renamed at some point in the last six months. \Twig_SimpleTest works in place of TwigTest above. See: twig.sensiolabs.org/doc/advanced.html#tests
  • KingCrunch
    KingCrunch about 10 years
    @TwoWholeWorms Nope, I just like to import them like use \Twig_Test as TwigTest; // or even "as Test" (and it was copy-pasted from somewhere). Fits better into the overall coding style
  • KingCrunch
    KingCrunch about 10 years
    For those, who want to know: Thats called "Duck-Typing" en.wikipedia.org/wiki/Duck_typing :)
  • Kristian Sandström
    Kristian Sandström about 10 years
    Wouldn't the above solution work just fine, since it's a DateTime you know it has the ->format method among other things. If i understand your example something like if entity.format is defined should do the trick!
  • juanmf
    juanmf about 10 years
    I, I was worried about the heading type. I replaced the <TD> innerHtml by an included template that so far looks like: {% if bridge.is_scalar(attribute(entity, heading)) or attribute(entity, heading).__toString is defined or attribute(entity, heading) is null %} {{ attribute(entity, heading) ? : 'empty'}} {% else %} {% if bridge.get_class(attribute(entity, heading)) == 'DateTime' %} {#{ dump(bridge.get_class(attribute(entity, heading))) }#} {{ attribute(entity, heading) | date(dateFormat) }} {% else %} {% endif %} {% endif %}
  • juanmf
    juanmf about 10 years
    here is Bridge: namespace DocDigital\Bundle\DocumentBundle\Helper; /** * Nasty stuff to support needed php fns in twig * * @author Juan Manuel Fernandez <[email protected]> */ class TwigPhpBridge { public function __call($functionName, array $argV) { return call_user_func_array($functionName, $argV); } }
  • tlorens
    tlorens almost 4 years
    I beg to differ, if I have all my associated items and I want to list them on the same page or display a count, why should I filter in my controller and have to pass yet more variables to the view when the view already has these items and just need to be broken out for display purposes?